@wuwei-labs/srsly
Version:
TypeScript SDK for SRSLY
157 lines • 4.63 kB
JavaScript
/**
* @purpose Payment schedule parameter conversion utilities
*
* Converts user-friendly schedule formats to strings for use with Solana programs.
* Supports common shortcuts and cron expressions.
*/
/**
* Common schedule shortcuts
*/
export const SCHEDULE_SHORTCUTS = [
'@hourly',
'@daily',
'@weekly',
'@monthly',
'@yearly',
'@annually',
'@decasecond',
'@minute',
'hourly',
'daily',
'weekly',
'monthly',
'yearly',
'annually',
'decasecond',
'minute',
];
/**
* Mapping of shortcuts without @ prefix to their @ equivalents
*/
const SHORTCUT_TO_AT_PREFIX = {
hourly: '@hourly',
daily: '@daily',
weekly: '@weekly',
monthly: '@monthly',
yearly: '@yearly',
annually: '@annually',
decasecond: '@decasecond',
minute: '@minute',
};
/**
* Basic cron expression validation pattern
*
* Supports:
* - Standard 5-field cron: "0 * * * *"
* - Extended 6-field cron with seconds: "0 0 * * * *"
* - @shortcuts: "@hourly", "@daily", etc.
*/
const CRON_PATTERN = /^(@\w+|[\d\*\/\-,]+(\s+[\d\*\/\-,]+){4,5})$/;
/**
* Normalize payment schedule input to program-compatible format
*
* @param schedule - Payment schedule string
* @returns Normalized schedule string for Solana program
* @throws Error if schedule format is invalid
*
* @example
* ```typescript
* normalizeSchedule('daily') // '@daily'
* normalizeSchedule('@hourly') // '@hourly'
* normalizeSchedule('0 * * * *') // '0 * * * *'
* normalizeSchedule('0 0 * * * *') // '0 0 * * * *' (with seconds)
* ```
*/
export function normalizeSchedule(schedule) {
if (!schedule || typeof schedule !== 'string') {
throw new Error(`Invalid schedule format. Expected string, got: ${typeof schedule}`);
}
// Trim whitespace
const trimmed = schedule.trim();
if (!trimmed) {
throw new Error('Schedule cannot be empty');
}
// If it already has @ prefix, validate and return
if (trimmed.startsWith('@')) {
if (!validateScheduleFormat(trimmed)) {
throw new Error(`Invalid schedule shortcut: ${trimmed}`);
}
return trimmed.toLowerCase();
}
// Check if it's a known shortcut without @ prefix
const lowerTrimmed = trimmed.toLowerCase();
if (lowerTrimmed in SHORTCUT_TO_AT_PREFIX) {
return SHORTCUT_TO_AT_PREFIX[lowerTrimmed];
}
// Otherwise treat as cron expression - validate and return as-is
if (!validateScheduleFormat(trimmed)) {
throw new Error(`Invalid schedule format. Expected @shortcut or cron expression. ` +
`Valid shortcuts: ${Object.keys(SHORTCUT_TO_AT_PREFIX).join(', ')}. ` +
`Got: "${trimmed}"`);
}
return trimmed;
}
/**
* Validate schedule format
*
* Checks if a schedule string is either:
* - A valid @shortcut
* - A valid cron expression (5 or 6 fields)
*
* @param schedule - Schedule string to validate
* @returns True if valid, false otherwise
*
* @example
* ```typescript
* validateScheduleFormat('@daily') // true
* validateScheduleFormat('0 * * * *') // true
* validateScheduleFormat('invalid') // false
* ```
*/
export function validateScheduleFormat(schedule) {
if (!schedule || typeof schedule !== 'string') {
return false;
}
const trimmed = schedule.trim();
// Check if it's a @shortcut
if (trimmed.startsWith('@')) {
const shortcut = trimmed.toLowerCase();
return SCHEDULE_SHORTCUTS.includes(shortcut);
}
// Check if it matches cron pattern
return CRON_PATTERN.test(trimmed);
}
/**
* Convert schedule string to human-readable description
*
* @param schedule - Normalized schedule string
* @returns Human-readable description
*
* @example
* ```typescript
* formatSchedule('@daily') // "Daily"
* formatSchedule('@hourly') // "Hourly"
* formatSchedule('0 * * * *') // "Custom: 0 * * * *"
* ```
*/
export function formatSchedule(schedule) {
const trimmed = schedule.trim().toLowerCase();
// Handle @shortcuts
if (trimmed.startsWith('@')) {
const shortcut = trimmed.slice(1); // Remove @
return shortcut.charAt(0).toUpperCase() + shortcut.slice(1);
}
// Handle cron expressions
return `Custom: ${trimmed}`;
}
/**
* Check if a schedule is a known shortcut
*
* @param schedule - Schedule string to check
* @returns True if it's a shortcut (with or without @)
*/
export function isScheduleShortcut(schedule) {
const lower = schedule.toLowerCase();
return SCHEDULE_SHORTCUTS.includes(lower);
}
//# sourceMappingURL=schedule.js.map