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

chadtmiller.com/pds.js

An atproto PDS in JavaScript, for Cloudflare Workers and Node

0
0
main
73 lines · 1.9 KB Raw History
// @pdsjs/core/validation - Reusable validation utilities

/**
 * DID format regex - supports did:plc and did:web
 * did:plc uses base32 (a-z, 2-7) with 24 chars
 */
export const DID_REGEX = /^did:(plc:[a-z2-7]{24}|web:.+)$/;

/**
 * Handle format regex - domain-like format
 * Each label: starts/ends alphanumeric, can have hyphens in middle, 1-63 chars
 * Must have at least 2 labels (e.g., user.bsky.social)
 */
export const HANDLE_REGEX =
  /^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+$/;

/**
 * Hex string regex
 */
export const HEX_REGEX = /^[0-9a-fA-F]+$/;

/**
 * Validate DID format
 * @param {unknown} did
 * @returns {did is string}
 */
export function isValidDid(did) {
  return typeof did === 'string' && DID_REGEX.test(did);
}

/**
 * Validate handle format
 * @param {unknown} handle
 * @returns {handle is string}
 */
export function isValidHandle(handle) {
  return typeof handle === 'string' && HANDLE_REGEX.test(handle);
}

/**
 * Validate hex string
 * @param {unknown} str
 * @returns {str is string}
 */
export function isValidHex(str) {
  return typeof str === 'string' && HEX_REGEX.test(str);
}

/**
 * Validate private key bytes (32 bytes for secp256k1)
 * @param {Uint8Array} key
 * @returns {boolean}
 */
export function isValidPrivateKeyLength(key) {
  return key instanceof Uint8Array && key.length === 32;
}

/**
 * Parse and validate a numeric cursor (non-negative integer)
 * @param {string|null} cursor - Cursor string from URL params
 * @returns {{valid: true, value: number} | {valid: false, error: string} | null}
 */
export function parseNumericCursor(cursor) {
  if (cursor === null || cursor === '') return null;
  const num = parseInt(cursor, 10);
  if (Number.isNaN(num)) {
    return { valid: false, error: 'cursor must be a valid integer' };
  }
  if (num < 0) {
    return { valid: false, error: 'cursor must not be negative' };
  }
  return { valid: true, value: num };
}