Files
indiekit-endpoint-microsub/lib/utils/validation.js
Ricardo 30f9939b3a feat: initial commit - Microsub endpoint for Indiekit
Fork of @indiekit/endpoint-microsub with customizations.
Enables subscribing to feeds and reading content using the Microsub protocol.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 16:32:55 +01:00

130 lines
3.0 KiB
JavaScript

/**
* Input validation utilities for Microsub
* @module utils/validation
*/
import { IndiekitError } from "@indiekit/error";
/**
* Valid Microsub actions (PR 1: channels and timeline only)
*/
export const VALID_ACTIONS = ["channels", "timeline"];
/**
* Validate action parameter
* @param {string} action - Action to validate
* @throws {IndiekitError} If action is invalid
*/
export function validateAction(action) {
if (!action) {
throw new IndiekitError("Missing required parameter: action", {
status: 400,
});
}
if (!VALID_ACTIONS.includes(action)) {
throw new IndiekitError(`Invalid action: ${action}`, {
status: 400,
});
}
}
/**
* Validate channel UID
* @param {string} channel - Channel UID to validate
* @param {boolean} [required] - Whether channel is required
* @throws {IndiekitError} If channel is invalid
*/
export function validateChannel(channel, required = true) {
if (required && !channel) {
throw new IndiekitError("Missing required parameter: channel", {
status: 400,
});
}
if (channel && typeof channel !== "string") {
throw new IndiekitError("Invalid channel parameter", {
status: 400,
});
}
}
/**
* Validate entry/entries parameter
* @param {string|Array} entry - Entry ID(s) to validate
* @returns {Array} Array of entry IDs
* @throws {IndiekitError} If entry is invalid
*/
export function validateEntries(entry) {
if (!entry) {
throw new IndiekitError("Missing required parameter: entry", {
status: 400,
});
}
// Normalize to array
const entries = Array.isArray(entry) ? entry : [entry];
if (entries.length === 0) {
throw new IndiekitError("Entry parameter cannot be empty", {
status: 400,
});
}
return entries;
}
/**
* Validate channel name
* @param {string} name - Channel name to validate
* @throws {IndiekitError} If name is invalid
*/
export function validateChannelName(name) {
if (!name || typeof name !== "string") {
throw new IndiekitError("Missing required parameter: name", {
status: 400,
});
}
if (name.length > 100) {
throw new IndiekitError("Channel name must be 100 characters or less", {
status: 400,
});
}
}
/**
* Parse array parameter from request
* Handles both array[] and array[0], array[1] formats
* @param {object} body - Request body
* @param {string} parameterName - Parameter name
* @returns {Array} Parsed array
*/
export function parseArrayParameter(body, parameterName) {
// Direct array
if (Array.isArray(body[parameterName])) {
return body[parameterName];
}
// Single value
if (body[parameterName]) {
return [body[parameterName]];
}
// Indexed values (param[0], param[1], ...)
const result = [];
let index = 0;
while (body[`${parameterName}[${index}]`] !== undefined) {
result.push(body[`${parameterName}[${index}]`]);
index++;
}
// Array notation (param[])
if (body[`${parameterName}[]`]) {
const values = body[`${parameterName}[]`];
return Array.isArray(values) ? values : [values];
}
return result;
}