UX Helpers: Consistent User Experience
Shared UX utilities for consistent experience across all PRIVATE.ME packages. Provides standardized pagination, search, progress reporting, and error formatting — ensuring every ACI delivers the same developer-friendly interface. Zero configuration. One dependency (@private.me/shared). Tested across 100+ ACIs.
Executive Summary
UX Helpers provides four essential capabilities that every developer expects: pagination with metadata, flexible search, progress tracking, and actionable error messages. Used by 100+ ACIs across the PRIVATE.ME platform.
Pagination handles collections of any size with consistent metadata (page, limit, total, hasNext, hasPrev). Search supports case-insensitive matching across multiple fields with optional result limits. Progress reporting enables real-time updates for long-running operations like XorIDA splitting or multi-share reconstruction. Error formatting transforms technical errors into user-friendly messages with hints, field attribution, and documentation links.
Every ACI that integrates UX Helpers delivers the same developer experience — predictable interfaces, helpful error messages, and progress visibility. This consistency reduces cognitive load when working across multiple ACIs and makes the entire platform feel cohesive.
Zero configuration required. Import, call, done. Works anywhere JavaScript runs — Node.js, Deno, Bun, Cloudflare Workers, browsers. Dual ESM and CJS builds ship in a single package.
Developer Experience
UX Helpers prioritizes developer productivity through consistent interfaces, comprehensive TypeScript types, and helpful error messages across all 100+ ACIs.
Progress Callbacks
Every long-running operation supports ProgressCallback for real-time status updates. Progress reporters work in both single-stage and multi-stage scenarios, automatically calculating percentages based on operation progress.
import { createStagedProgress } from '@private.me/ux-helpers'; async function deployService(onProgress?: ProgressCallback) { const progress = createStagedProgress( ['Building', 'Testing', 'Deploying'], onProgress ); progress.start(); // "Building" (0%) await build(); progress.nextStage(); // "Testing" (33%) await test(); progress.nextStage(); // "Deploying" (66%) await deploy(); progress.complete(); // "Deploying" (100%) }
Structured Error Handling
All error utilities follow the ACIErrorDetail interface with machine-readable codes, human-readable messages, actionable hints, and documentation links. This standardization means developers can handle errors consistently across all ACIs.
import { createDetailedError, formatErrorForUser } from '@private.me/ux-helpers'; const error = createDetailedError( 'INVALID_PAGE', 'Page number must be at least 1', { hint: 'Provide a positive page number (e.g., page: 1)', field: 'page', docs: 'https://private.me/docs/pagination' } ); console.log(formatErrorForUser(error)); // Page number must be at least 1 // Field: page // Hint: Provide a positive page number (e.g., page: 1) // Docs: https://private.me/docs/pagination
Type Safety
Every function includes comprehensive TypeScript types with readonly arrays, strict null checks, and generic type parameters. IDEs provide full autocomplete and inline documentation for all exports.
The Problem
Without shared UX primitives, every package implements pagination differently, error messages vary wildly in quality, progress reporting is inconsistent, and search behavior is unpredictable.
Pagination chaos. Some packages return arrays, others return objects. Page numbers start at 0 in one package, 1 in another. Total counts are missing. "Next page" logic requires manual calculation.
Error messages are useless. Generic "Error: invalid input" with no context. No hints on how to fix the problem. No documentation links. Developers resort to reading source code to understand what went wrong.
Progress is invisible. Long operations (XorIDA splitting, multi-share reconstruction, batch processing) appear frozen. No way to track completion percentage. No intermediate status updates.
Search is reinvented poorly. Every package implements its own fuzzy matching. Case sensitivity is inconsistent. Field filtering syntax varies. Result limits are hard-coded or missing.
| Property | Without UX Helpers | With UX Helpers |
|---|---|---|
| Pagination | Every package different | Standardized metadata |
| Error messages | Generic, unhelpful | Actionable hints + docs |
| Progress tracking | Missing or ad-hoc | Consistent callbacks |
| Search behavior | Varies by package | Unified interface |
| Type safety | Partial or missing | Full TypeScript |
| Learning curve | High (each ACI different) | Low (one pattern) |
The Old Way
The New Way
Real-World Use Cases
Six scenarios where UX Helpers delivers consistent developer experience across diverse ACIs.
List 10,000+ registered DIDs with consistent page navigation. Metadata includes total count, current page, and hasNext flag for infinite scroll UIs.
paginate() + PaginatedResultFind DIDs by name, email, or scope across federated registries. Case-insensitive fuzzy matching with configurable result limits.
search() + fields + fuzzyTrack multi-share splitting and reconstruction. Real-time updates show "Splitting share 2/3" or "Reconstructing from 2 shares..."
ProgressReporter + onProgressConvert cryptic validation failures into actionable guidance. Field attribution, resolution hints, and doc links reduce support burden.
createDetailedError() + formatErrorForUser()Process 1000 envelopes with staged progress: "Encrypting" → "Signing" → "Sending". Users see exactly where they are in the pipeline.
createStagedProgress() + stages[]Search across millions of audit entries by DID, action, timestamp. Paginated results with search highlighting and total match counts.
search() + paginate() compositionSolution Architecture
Four independent modules. Each can be used standalone or composed for powerful UX patterns.
Pagination
paginate() slices a collection into pages and returns both the slice and metadata. createPaginationMetadata() generates metadata without slicing — useful for server-side pagination where you only return the current page.
import { paginate } from '@private.me/ux-helpers'; const allItems = [/* 1000 items */]; const result = paginate(allItems, { page: 2, limit: 20 }); console.log(result.data); // Items 21-40 console.log(result.pagination.hasNext); // true console.log(result.pagination.totalPages); // 50
Search
search() filters a collection based on a query string. Matches across multiple fields with case-insensitive fuzzy matching. Returns the filtered subset.
import { search } from '@private.me/ux-helpers'; const users = [ { name: 'Alice', email: 'alice@example.com', role: 'admin' }, { name: 'Bob', email: 'bob@example.com', role: 'user' }, ]; const results = search(users, { query: 'alice', fields: ['name', 'email'], limit: 10 }); // Returns: [{ name: 'Alice', ... }]
Progress Reporting
ProgressReporter class provides start/update/complete methods. createStagedProgress() divides operations into named stages and automatically calculates percentages.
import { createStagedProgress } from '@private.me/ux-helpers'; async function processData(onProgress?: ProgressCallback) { const progress = createStagedProgress( ['Validating', 'Encrypting', 'Uploading'], onProgress ); progress.start(); // "Validating" (0%) await validate(); progress.nextStage(); // "Encrypting" (33%) await encrypt(); progress.nextStage(); // "Uploading" (66%) await upload(); progress.complete(); // "Uploading" (100%) }
Error Formatting
createDetailedError() produces structured errors with machine-readable codes and human-readable messages. formatErrorForUser() and formatErrorForLog() render errors for different audiences.
import { createDetailedError, formatErrorForUser } from '@private.me/ux-helpers'; const error = createDetailedError( 'INVALID_PAGE', 'Page number must be at least 1', { hint: 'Page numbers start at 1. Try page: 1 instead.', field: 'page', docs: 'https://private.me/docs/pagination#page-numbers' } ); // For users: console.log(formatErrorForUser(error)); // For logs: console.error(formatErrorForLog(error));
Integration
Install once, import anywhere. Works in Node.js, Deno, Bun, Cloudflare Workers, and browsers.
Installation
pnpm add @private.me/ux-helpers
Basic Usage
import { paginate, search, ProgressReporter, createDetailedError } from '@private.me/ux-helpers'; // Paginate const page1 = paginate(items, { page: 1, limit: 50 }); // Search const results = search(users, { query: 'admin', fields: ['role'] }); // Progress const progress = new ProgressReporter((status, pct) => { console.log(`${status}: ${pct}%`); }); progress.start('Initializing'); progress.update('Processing', 50); progress.complete(); // Errors const error = createDetailedError('INVALID_INPUT', 'Field is required');
CommonJS
Dual ESM/CJS builds support both import and require.
const { paginate, search } = require('@private.me/ux-helpers');
Security
UX Helpers is a pure utility library with no cryptographic operations. Security considerations focus on input validation and preventing information leakage through error messages.
Input Validation
Pagination bounds: Page numbers clamped to [1, totalPages]. Limit clamped to [1, 100]. Negative or non-numeric inputs rejected with clear error messages.
Search sanitization: Query strings are not evaluated or executed. Field names are validated against object keys. No regex injection risk — queries are literal string matching.
Error Message Safety
No stack traces in user-facing errors. formatErrorForUser() strips technical details. formatErrorForLog() includes full context for server-side logging only.
Field attribution: Error messages never leak sensitive field values. Only field names are included (e.g., "Field: email" not "Field: alice@example.com").
Progress Callback Isolation
Progress callbacks are optional and never block operations. Callback exceptions are caught and logged, preventing denial-of-service via malicious callback implementations.
createPaginationMetadata() to avoid memory exhaustion.
Benchmarks
Performance measurements for common operations on realistic dataset sizes.
| Operation | Dataset Size | Latency | Throughput |
|---|---|---|---|
| paginate() | 100,000 items | <1ms | Constant time (slice only) |
| createPaginationMetadata() | N/A | <0.1ms | Pure calculation, no I/O |
| search() (single field) | 10,000 items | ~5ms | 2,000 items/ms |
| search() (3 fields) | 10,000 items | ~12ms | 833 items/ms |
| ProgressReporter.update() | N/A | <0.01ms | Callback execution only |
| createDetailedError() | N/A | <0.01ms | Object creation only |
| formatErrorForUser() | N/A | <0.1ms | String formatting only |
Memory Usage
Benchmarks run on Node.js v22.0, Intel i7-12700K, 32GB RAM. Measurements via process.hrtime.bigint(). Real-world performance depends on JavaScript engine, dataset characteristics, and garbage collection.
Honest Limitations
UX Helpers is intentionally simple. Here's what it doesn't do and when you should choose alternatives.
Pagination
Client-side only by default. paginate() loads the entire collection into memory. For datasets >100,000 items, implement server-side pagination and use createPaginationMetadata() to generate the metadata without slicing.
No cursor-based pagination. Page numbers only. For infinite scroll with stable cursors (e.g., "messages after timestamp X"), implement your own cursor logic.
Search
Linear scan. Search is O(n) over the entire collection. For large datasets (>10,000 items), use a real search engine (Elasticsearch, Meilisearch) or database full-text index.
No stemming, no fuzzy distance. Matching is simple case-insensitive substring search. No Levenshtein distance, no phonetic matching, no query parsing (AND/OR/NOT).
In-memory only. Cannot search across distributed shards or paginated API results. You must fetch all data before searching.
Progress Reporting
Synchronous callbacks only. ProgressCallback is void-returning. Cannot await promises. For async progress sinks (WebSocket, SSE), wrap the callback and queue updates.
No progress persistence. If the process crashes mid-operation, progress is lost. For resumable operations, store progress externally (database, Redis).
Error Formatting
English only. Error messages are hardcoded in English. For internationalization, map error codes to localized strings in your application layer.
No error recovery. This package formats errors but does not provide retry logic, circuit breakers, or automatic fallbacks.
Appendices
Type definitions, full API surface, error taxonomy, and codebase statistics.
Type Definitions
Complete TypeScript interfaces for all UX Helpers exports.
interface PaginationOptions { readonly page?: number; // 1-based, default: 1 readonly limit?: number; // default: 50, max: 100 } interface PaginatedResult<T> { readonly data: readonly T[]; readonly pagination: { readonly page: number; readonly limit: number; readonly total: number; readonly totalPages: number; readonly hasNext: boolean; readonly hasPrev: boolean; }; } interface SearchOptions { readonly query: string; readonly fields?: readonly string[]; // optional field list readonly fuzzy?: boolean; // default: true readonly limit?: number; // max results } type ProgressCallback = (status: string, percent?: number) => void; interface ACIErrorDetail { readonly code: string; // Machine-readable error code readonly message: string; // Human-readable description readonly hint?: string; // Actionable resolution hint readonly field?: string; // Field that caused the error readonly docs?: string; // Documentation URL }
Full API Surface
Complete function signatures organized by module.
Pagination
Slice collection into pages and return data + metadata. Client-side pagination.
Generate pagination metadata without slicing. For server-side pagination.
Search
Filter collection by query string. Supports multi-field fuzzy matching.
Progress Reporting
Create progress reporter with optional callback.
Report progress update.
Report start (0%).
Update progress to specific percentage.
Report completion (100%).
Create multi-stage progress reporter with automatic percentage calculation.
Error Formatting
Create structured error with code, message, hint, field, and docs URL.
Format error for display to end users. Excludes technical details.
Format error for server-side logging. Includes error code and all fields.
Type guard to check if an error is ACIErrorDetail.
Convert any error (Error, string, unknown) to ACIErrorDetail.
Error Taxonomy
Standard error codes returned by UX Helpers utilities.
| Code | Module | When |
|---|---|---|
| INVALID_PAGE | Pagination | Page number < 1 or non-numeric |
| INVALID_LIMIT | Pagination | Limit < 1, > 100, or non-numeric |
| EMPTY_QUERY | Search | Search query is empty string |
| INVALID_FIELD | Search | Field does not exist on objects |
| INVALID_PERCENT | Progress | Percent < 0 or > 100 |
| NO_STAGES | Progress | Stage array is empty |
| STAGE_OVERFLOW | Progress | nextStage() called beyond final stage |
| INVALID_ERROR_CODE | Errors | Error code is empty or whitespace-only |
| MISSING_MESSAGE | Errors | Error message is empty |
Codebase Stats
UX Helpers v0.1.0 — lightweight, well-tested, zero dependencies.
Module Inventory
| Module | Source File | Purpose |
|---|---|---|
| Pagination | pagination.ts | paginate() + createPaginationMetadata() |
| Search | search.ts | Multi-field fuzzy search |
| Progress | progress.ts | ProgressReporter + createStagedProgress() |
| Errors | errors.ts | createDetailedError() + format helpers |
| Types | types.ts | Shared TypeScript interfaces |
| Index | index.ts | Barrel exports |
Usage Across Platform
UX Helpers is integrated into 100+ ACIs across the PRIVATE.ME platform. Every package with pagination, search, or long-running operations uses these shared primitives to deliver consistent developer experience.
Deployment Options
SaaS Recommended
Fully managed infrastructure. Call our REST API, we handle scaling, updates, and operations.
- Zero infrastructure setup
- Automatic updates
- 99.9% uptime SLA
- Enterprise SLA available
SDK Integration
Embed directly in your application. Runs in your codebase with full programmatic control.
npm install @private.me/ux-helpers- TypeScript/JavaScript SDK
- Full source access
- Enterprise support available
On-Premise Upon Request
Enterprise CLI for compliance, air-gap, or data residency requirements.
- Complete data sovereignty
- Air-gap capable deployment
- Custom SLA + dedicated support
- Professional services included
Enterprise On-Premise Deployment
While UX Helpers is primarily delivered as SaaS or SDK, we build dedicated on-premise infrastructure for customers with:
- Regulatory mandates — HIPAA, SOX, FedRAMP, CMMC requiring self-hosted processing
- Air-gapped environments — SCIF, classified networks, offline operations
- Data residency requirements — EU GDPR, China data laws, government mandates
- Custom integration needs — Embed in proprietary platforms, specialized workflows
Includes: Enterprise CLI, Docker/Kubernetes orchestration, RBAC, audit logging, and dedicated support.