// src/lib/verifications.js — pure helpers for the Verifier component. // Derive an rkey from a record uri: at://did/collection/rkey export const rkeyFromUri = (uri) => (uri ? uri.split('/').pop() : null); // Deterministic rkey for a verification record: "v-" + base64url(subject DID). // Atproto rkeys allow [A-Za-z0-9._~-]; base64url output is only A-Za-z0-9_-, // so this is always valid and collision-free per subject — putRecord with this // rkey is an idempotent upsert (re-verifying the same DID never duplicates). export const verificationRkey = (subjectDid) => 'v-' + btoa(subjectDid).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, ''); // Split an array into chunks of at most `size` (last chunk may be smaller). export const chunkArray = (items, size) => { if (!Array.isArray(items) || items.length === 0) return []; if (!Number.isInteger(size) || size < 1) throw new Error('chunkArray: size must be a positive integer'); const chunks = []; for (let i = 0; i < items.length; i += size) { chunks.push(items.slice(i, i + size)); } return chunks; }; /** * Staleness decision for one verification, given the stored record and the * account's current profile (or null when the account no longer resolves). * * @param {Object} stored {handle, displayName} * @param {Object|null} profile current profile {did, handle, displayName} * @returns {{stale: boolean, gone: boolean, currentHandle: string|null, currentDisplayName: string|null}} */ export const assessProfile = (stored, profile) => { if (!profile) { return { stale: false, gone: true, currentHandle: null, currentDisplayName: null }; } const currentHandle = profile.handle; const currentDisplayName = profile.displayName || currentHandle; return { stale: currentHandle !== stored.handle || currentDisplayName !== stored.displayName, gone: false, currentHandle, currentDisplayName, }; };