store.js
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.loadAlerts = loadAlerts;
exports.saveAlerts = saveAlerts;
exports.addAlert = addAlert;
exports.updateAlert = updateAlert;
exports.deleteAlert = deleteAlert;
exports.getDueAlerts = getDueAlerts;
exports.parseRelativeTime = parseRelativeTime;
const promises_1 = require("fs/promises");
const path_1 = require("path");
const fs_1 = require("fs");
function generateId() {
return Date.now().toString(36) + Math.random().toString(36).slice(2, 6);
}
function alertsPath(dataPath) {
return (0, path_1.join)(dataPath, "alerts.json");
}
async function loadAlerts(dataPath) {
const path = alertsPath(dataPath);
if (!(0, fs_1.existsSync)(path))
return [];
try {
const raw = await (0, promises_1.readFile)(path, "utf-8");
return JSON.parse(raw);
}
catch {
return [];
}
}
async function saveAlerts(dataPath, alerts) {
await (0, promises_1.mkdir)(dataPath, { recursive: true });
await (0, promises_1.writeFile)(alertsPath(dataPath), JSON.stringify(alerts, null, 2), "utf-8");
}
async function addAlert(dataPath, message, dueAt, priority, tags) {
const alerts = await loadAlerts(dataPath);
const alert = {
id: generateId(),
message,
dueAt: dueAt.toISOString(),
createdAt: new Date().toISOString(),
status: "pending",
priority,
tags,
};
alerts.push(alert);
await saveAlerts(dataPath, alerts);
return alert;
}
async function updateAlert(dataPath, id, patch) {
const alerts = await loadAlerts(dataPath);
const idx = alerts.findIndex(a => a.id === id);
if (idx === -1)
return null;
alerts[idx] = { ...alerts[idx], ...patch };
await saveAlerts(dataPath, alerts);
return alerts[idx];
}
async function deleteAlert(dataPath, id) {
const alerts = await loadAlerts(dataPath);
const filtered = alerts.filter(a => a.id !== id);
if (filtered.length === alerts.length)
return false;
await saveAlerts(dataPath, filtered);
return true;
}
/** Returns alerts that are due or overdue and still pending (or snoozed but snooze expired). */
function getDueAlerts(alerts) {
const now = new Date();
return alerts.filter(a => {
if (a.status === "dismissed")
return false;
if (a.status === "snoozed" && a.snoozedUntil) {
return new Date(a.snoozedUntil) <= now;
}
return new Date(a.dueAt) <= now;
});
}
/** Parse natural language time expressions into a Date. */
function parseRelativeTime(expr) {
const now = new Date();
const s = expr.trim().toLowerCase();
// Absolute ISO date/datetime
if (/^\d{4}-\d{2}-\d{2}/.test(s))
return new Date(s);
// Relative: "in 30 minutes", "in 2 hours", "in 3 days", "tomorrow", "tonight"
const inMatch = s.match(/^in\s+(\d+(?:\.\d+)?)\s+(minute|hour|day|week)s?$/);
if (inMatch) {
const n = parseFloat(inMatch[1]);
const unit = inMatch[2];
const ms = unit === "minute" ? n * 60_000
: unit === "hour" ? n * 3_600_000
: unit === "day" ? n * 86_400_000
: n * 7 * 86_400_000;
return new Date(now.getTime() + ms);
}
if (s === "tomorrow") {
const d = new Date(now);
d.setDate(d.getDate() + 1);
d.setHours(9, 0, 0, 0);
return d;
}
if (s === "tonight") {
const d = new Date(now);
d.setHours(20, 0, 0, 0);
return d;
}
if (s === "end of day" || s === "eod") {
const d = new Date(now);
d.setHours(17, 0, 0, 0);
return d;
}
// Try native Date parse as fallback
const parsed = new Date(expr);
if (!isNaN(parsed.getTime()))
return parsed;
throw new Error(`Cannot parse time expression: "${expr}". Use ISO date or phrases like "in 2 hours", "tomorrow", "in 3 days".`);
}