@vicaniddouglas/js_aide
v1.19.0
Published
A versatile collection of modular JavaScript utility helpers designed to streamline single-page application (SPA) development and general web programming tasks.
Maintainers
Readme
@vicaniddouglas/js_aide
JS Aide is a high-performance, modular utility collection designed for modern web applications. Whether you're building a native SPA or an enterprise-grade MPA, JS Aide provides the orchestrated power you need.
🕹️ Live Playground
Explore all library features—including the new Asynchronous Handshaking system—directly in our Interactive Sandbox.
Router
A modern, feature-rich client-side router for Single-Page Applications (SPAs). Features include History API navigation, dynamic route segments (/users/:id), and view lifecycle hooks (beforeEnter, beforeLeave).
Validator
A collection of high-precision validation utilities. Handles complex email logic, phone number formatting, and mandatory field checks out of the box.
Input Handlers
Utilities for enhanced DOM interaction, such as restricting input to specific characters, real-time number formatting, and custom select behaviors.
HTTP Engine
A "Zero-Touch" HTTP client built on sendRequest. Features automatic retries with exponential backoff, request queuing, and standardized response objects.
Dates & Timing
Convenience functions for complex date manipulation, including relative timing (timeAgo), humanizeDate, and range calculations.
File Manager
Robust utilities for browser-side file handling, including dynamic DataExporter for Excel/PDF and secure file upload orchestration.
Camera
Advanced module for interacting with device cameras, allowing image capture and video stream management with a single promise-based API.
Figures & Formatting
Specialized formatting for financial data. Includes native support for UGX (Ugandan Shilling) currency defaults and abbreviated large numbers.
Dependency Manager
A smart loader for external libraries (like SheetJS or jsPDF). Manages CDN fallbacks, retry logic, and ensures libraries are only loaded when needed.
WebSocket Client
A professional-grade WebSocket implementation with automatic reconnection, heartbeat monitoring, and a registry to prevent duplicate connections.
Installation
Install the package via npm:
npm install @vicaniddouglas/js_aideOr via yarn:
yarn add @vicaniddouglas/js_aideUsage
Ensure your project is configured to use ES Modules (e.g., by adding "type": "module" to your project's package.json or using a bundler).
Importing Modules
You can import specific modules or all of them:
// Import specific modules for best tree-shaking
import {
Router,
sendRequest,
validatePhoneNumber,
humanizeDate,
} from "@vicaniddouglas/js_aide";Example: Using the Router
// main.js
import { Router } from "@vicaniddouglas/js_aide";
// Define your routes, mapping paths to DOM element IDs
const appRoutes = {
"/": "home-view",
"/products": "products-list-view",
"/products/:id": "product-detail-view", // Dynamic segment
"/profile": "user-profile-view",
};
const router = new Router(appRoutes, "/");
// --- Optional: Add a route guard ---
router.beforeNavigate = async (toPath, navigationContext) => {
console.log(
`Attempting to navigate from ${navigationContext.from} to ${toPath}`,
);
// Example: Protect the profile route
if (toPath.startsWith("/profile")) {
const isAuthenticated = await checkUserAuthentication(); // Your auth logic
if (!isAuthenticated) {
console.warn(
"Access denied: User not authenticated. Redirecting to login.",
);
return "/login"; // Redirect to a login page
}
}
return true; // Allow navigation
};
// --- Optional: Listen for navigation events ---
window.addEventListener("router:navigated", (event) => {
const { path, pathname, params, query, hash } = event.detail;
console.log(`Navigated to: ${path}`);
console.log("Params:", params); // e.g., { id: '123' } for /products/123
console.log("Query:", query); // e.g., { search: 'item' } for /search?search=item
console.log("Hash:", hash); // e.g., '#section1' for /page#section1
});
// --- Example: Define view lifecycle hooks on your DOM elements ---
// Assuming you have a <div id="product-detail-view"> in your HTML
const productDetailView = document.getElementById("product-detail-view");
if (productDetailView) {
productDetailView.beforeEnter = async (routeParams) => {
console.log(`Entering product detail view for ID: ${routeParams.id}`);
// Fetch product data based on routeParams.id
const product = await fetchProductData(routeParams.id);
// Render product details into the view
renderProductDetails(productDetailView, product);
};
productDetailView.beforeLeave = () => {
console.log("Leaving product detail view. Cleaning up...");
// Dispose of any event listeners or resources specific to this view
cleanupProductDetailResources();
};
}
// Manually navigate (or click an <a> tag)
router.navigate("/products/123?utm_source=email#overview");Example: Using the WebSocket Client
The WebSocketClient is designed to be "Zero-Touch," handling connection lifecycles and resource management automatically.
import { WebSocketClient, ConnectionEvent } from "@vicaniddouglas/js_aide";
// The constructor automatically reuses instances for the same URL (Registry Pattern)
const ws = new WebSocketClient("ws://localhost:8000");
// Listen for connection events
ws.on("open", () => {
console.log("Connected to server!");
});
// Register a handler for a specific event
ws.onMessage("user_update", (data) => {
console.log("Profile updated:", data);
});
ws.connect();ws.send(event, data) vs ws.sendWithAck(event, data)
ws.send(): A "fire and forget" method. It sends the data and returns immediately. Use this for non-critical updates like typing indicators.ws.sendWithAck(): Returns a Promise that resolves when the server confirms receipt. Use this for critical actions like saving a profile or processing a payment.
// Critical action with Acknowledgement
try {
const response = await ws.sendWithAck("save_profile", { bio: "Hello world" });
console.log("Profile saved successfully:", response);
} catch (error) {
console.error("Failed to save profile (timed out):", error.message);
}The Backend Contract (Implementing Acknowledgements)
To support sendWithAck(), your backend (e.g., Python/FastAPI, Node.js, Go) must follow the @vicaniddouglas framework standard:
- The client sends a message containing an
idandack: true. - The server must respond with a message containing:
event: "ack"id: The exact same ID sent by the client.success:trueorfalse.data(optional): Any response data to return to the client.
Example Server Response (JSON):
{
"event": "ack",
"id": "1712065432123-0",
"success": true,
"data": { "status": "saved" }
}Example: Using the Requests Module
The sendRequest function is a proactive HTTP client that standardizes all responses into a single dictionary format. It automatically handles error popups and callbacks, eliminating the need for try-catch blocks.
Standardized Response
Every call returns a Promise that always resolves to this structure:
{
status: boolean, // true if Success, false if Error (Network or Business)
log: string, // Error message on failure, or "" on success
data: any, // The actual payload from the server
httpCode: number // The raw HTTP status code
}New Proactive Options
| Option | Type | Default | Description |
| :------------ | :--------- | :------ | :--------------------------------------------------------------- |
| onSuccess | Function | null | Runs automatically if status is true. Strictly awaited. |
| onError | Function | null | Runs automatically if status is false. Strictly awaited. |
| showLoading | Function | null | Called with true/false. Strictly awaited. |
| silent | Boolean | false | If true, disables the automatic Red Popup on error. |
Usage Patterns
1. The "Sequential Async Flow" (Awaiting Callbacks)
Because sendRequest strictly awaits your callbacks, you can safely use async/await inside them without worrying about race conditions or premature page reloads.
import { sendRequest, popup } from "@vicaniddouglas/js_aide";
await sendRequest({
endpoint: "/api/save",
onSuccess: async (data) => {
// This popup is BEAUTIFUL and BLOCKING.
// sendRequest will NOT finish until the user clicks OK.
await popup.success("Settings Saved!");
},
});
// This reload is GUARANTEED to happen only after the popup is closed.
window.location.reload();2. The "Fire and Forget" (Proactive Success) No need to check status if you only care about success. If it fails, the user gets a popup automatically.
import { sendRequest } from "@vicaniddouglas/js_aide";
sendRequest({
endpoint: "/api/settings",
onSuccess: (data) => console.log("Profile updated!", data),
});2. The "Sequential Chain" (Linear Await)
Since the promise never rejects, you can write linear logic without try-catch.
const res = await sendRequest({ endpoint: "/profile" });
if (res.status) {
// Since status is true, we know data is safe to use
const stats = await sendRequest({ endpoint: `/stats/${res.data.id}` });
if (stats.status) render(stats.data);
}3. Silent Background Tasks
Use silent: true for background checks where a popup would be annoying.
sendRequest({
endpoint: "/api/poll-notifications",
silent: true,
onSuccess: (count) => updateBadge(count),
});Example: Using the FileUploader
The FileUploader module provides standardized utilities for validating and uploading files. It supports both traditional binary (Multipart/Form-Data) and JSON-based Base64 uploads.
Base64 File Conversions (For Previews)
The toBase64 utility converts a raw File object into a Base64 string. It follows the "Always Resolve" pattern.
import { toBase64 } from "@vicaniddouglas/js_aide";
const res = await toBase64(myFile);
if (res.status) {
// res.data is the full data URL (e.g. data:image/png;base64,...)
myImagePreview.src = res.data;
} else {
console.error("Conversion failed:", res.log);
}Standard Binary Upload
By default, uploadFile sends files as a binary Multipart/Form-Data stream.
import { uploadFile, popup } from "@vicaniddouglas/js_aide";
await uploadFile({
files: document.getElementById("myFileInput").files[0],
endpoint: "/api/upload-avatar",
fieldName: "avatar",
onSuccess: () => popup.success("Avatar uploaded!"),
});Base64 JSON Upload
For APIs that require images to be embedded in a JSON payload, simply toggle the asBase64 flag.
import { uploadFile } from "@vicaniddouglas/js_aide";
await uploadFile({
files: selectedFiles,
endpoint: "/api/upload-as-json",
asBase64: true, // Automically converts to Base64 strings
stripPrefix: true, // Only send the raw Base64 data (no "data:image/..." prefix)
payload: {
userId: 123,
description: "Holiday photo",
},
});Example: Using the Popup System
The popup module provides a Promise-based modal system for showing professional alerts. It is fully accessible (supports the Escape key) and highly customizable.
import { popup, icons } from "@vicaniddouglas/js_aide";
// Basic success alert (auto-closes in 2.5s)
await popup.success("Profile saved!");Configuration Reference
Every popup method (.success(), .error(), .info()) accepts an optional options object as the second argument.
| Option | Type | Default | Description |
| :----------- | :-------- | :---------------- | :--------------------------------------------------------------------- |
| duration | Number | 2500ms - 4000ms | Time before the modal closes automatically. |
| persistent | Boolean | false | If true, the modal will stay on screen until manually closed. |
| closable | Boolean | true | If false, hides the close button and disables Esc/backdrop clicks. |
| icon | String | Theme default | A custom SVG string to override the default icon. |
| color | String | Theme default | A custom hex or CSS color for the icon and background tint. |
Common Usage Patterns
1. Sticky Info (Requires Acknowledgment)
await popup.info("Maintenance scheduled for midnight.", { persistent: true });2. Process Lock (Interaction Block) Use this to prevent users from navigating away during critical operations.
// Shows a modal with no close buttons
popup.info("Deleting account data...", { persistent: true, closable: false });
await api.deleteAccount();
popup.close(); // Programmatically remove the modal
await popup.success("Account deleted.");3. Custom Branding Override the icon and color to match specific visual needs.
popup.success("Photo Uploaded!", {
icon: icons.cameraIcon(),
color: "#3b82f6",
});4. Asynchronous Handshaking (Zero-Alert)
The popup system provides replacements for native confirm() and prompt() that are fully asynchronous, themeable, and non-blocking in the developer's logic (via await).
Confirmation Dialogs
Returns true on confirm, false on cancel/close.
const confirmed = await popup.confirm("Are you sure you want to delete this?");
if (confirmed) {
await deleteRecord();
await popup.success("Deleted!");
}Data-Entry Prompts
Returns the input value as a string on submit, or null if cancelled.
const name = await popup.prompt("Enter your display name:", {
placeholder: "e.g., Douglas",
inputType: "text", // Options: text, number, float, comms, currency, password
});
if (name) {
console.log("Hello", name);
}| Prompt Option | Description |
| :------------- | :------------------------------------------------------------------ |
| inputType | Controls input behavior (text, number, float, comms, currency, password). |
| defaultValue | Initial text inside the input. |
| submitText | Text for the action button (default: "Submit"). |
| cancelText | Text for the dismiss button (default: "Cancel"). |
Development
Running Tests
We use Vitest for unit testing. The library has over 80+ verified tests covering all core modules (Routing, Validation, WebSocket, Dates, etc.).
npm testBuilding for Production
Generates minified ESM, IIFE, and CJS bundles in the dist folder using esbuild.
npm run buildLicense
This project is licensed under the MIT License.
