@mrck-labs/vanaheim-shared 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,240 @@
1
+ /**
2
+ * Database Types for Vanaheim
3
+ *
4
+ * Shared type definitions matching the Supabase PostgreSQL schema.
5
+ * Used by both desktop and mobile apps.
6
+ */
7
+ interface ExpenseCategory {
8
+ id: string;
9
+ userId: string;
10
+ name: string;
11
+ icon: string;
12
+ color: string;
13
+ sortOrder: number;
14
+ isDefault: boolean;
15
+ createdAt: string;
16
+ updatedAt: string;
17
+ }
18
+ interface NewExpenseCategory {
19
+ name: string;
20
+ icon: string;
21
+ color: string;
22
+ sortOrder?: number;
23
+ isDefault?: boolean;
24
+ }
25
+ interface Expense {
26
+ id: string;
27
+ userId: string;
28
+ name: string;
29
+ amount: number;
30
+ currency: string;
31
+ frequency: string;
32
+ categoryId: string;
33
+ nextDueDate: string;
34
+ notes: string | null;
35
+ isShared: boolean;
36
+ sharePercentage: number;
37
+ isActive: boolean;
38
+ createdAt: string;
39
+ updatedAt: string;
40
+ category?: ExpenseCategory;
41
+ }
42
+ interface NewExpense {
43
+ name: string;
44
+ amount: number;
45
+ currency: string;
46
+ frequency: string;
47
+ categoryId: string;
48
+ nextDueDate: string;
49
+ notes?: string | null;
50
+ isShared?: boolean;
51
+ sharePercentage?: number;
52
+ isActive?: boolean;
53
+ }
54
+ interface IncomeCategory {
55
+ id: string;
56
+ userId: string;
57
+ name: string;
58
+ icon: string;
59
+ color: string;
60
+ sortOrder: number;
61
+ isDefault: boolean;
62
+ createdAt: string;
63
+ updatedAt: string;
64
+ }
65
+ interface NewIncomeCategory {
66
+ name: string;
67
+ icon: string;
68
+ color: string;
69
+ sortOrder?: number;
70
+ isDefault?: boolean;
71
+ }
72
+ interface Income {
73
+ id: string;
74
+ userId: string;
75
+ name: string;
76
+ amount: number;
77
+ currency: string;
78
+ frequency: string;
79
+ categoryId: string;
80
+ nextPayDate: string;
81
+ notes: string | null;
82
+ isActive: boolean;
83
+ createdAt: string;
84
+ updatedAt: string;
85
+ category?: IncomeCategory;
86
+ }
87
+ interface NewIncome {
88
+ name: string;
89
+ amount: number;
90
+ currency: string;
91
+ frequency: string;
92
+ categoryId: string;
93
+ nextPayDate: string;
94
+ notes?: string | null;
95
+ isActive?: boolean;
96
+ }
97
+ interface FocusCategory {
98
+ id: string;
99
+ userId: string;
100
+ name: string;
101
+ color: string;
102
+ sortOrder: number;
103
+ isDefault: boolean;
104
+ createdAt: string;
105
+ updatedAt: string;
106
+ }
107
+ interface NewFocusCategory {
108
+ name: string;
109
+ color: string;
110
+ sortOrder?: number;
111
+ isDefault?: boolean;
112
+ }
113
+ type FocusSessionStatus = 'active' | 'completed' | 'abandoned';
114
+ interface FocusSession {
115
+ id: string;
116
+ userId: string;
117
+ title: string;
118
+ categoryId: string | null;
119
+ targetMinutes: number;
120
+ actualSeconds: number;
121
+ status: FocusSessionStatus;
122
+ startedAt: string;
123
+ completedAt: string | null;
124
+ createdAt: string;
125
+ updatedAt: string;
126
+ category?: FocusCategory;
127
+ }
128
+ interface NewFocusSession {
129
+ title: string;
130
+ categoryId?: string | null;
131
+ targetMinutes: number;
132
+ actualSeconds?: number;
133
+ status?: FocusSessionStatus;
134
+ startedAt?: string;
135
+ completedAt?: string | null;
136
+ }
137
+ interface FocusSessionFilters {
138
+ categoryId?: string;
139
+ status?: FocusSessionStatus;
140
+ startedAfter?: string;
141
+ startedBefore?: string;
142
+ }
143
+ type ChatMessageRole = 'user' | 'assistant' | 'system';
144
+ interface ChatConversation {
145
+ id: string;
146
+ userId: string;
147
+ title: string;
148
+ createdAt: string;
149
+ updatedAt: string;
150
+ }
151
+ interface NewChatConversation {
152
+ title: string;
153
+ }
154
+ interface ChatMessage {
155
+ id: string;
156
+ conversationId: string;
157
+ role: ChatMessageRole;
158
+ content: string;
159
+ metadata: string | null;
160
+ createdAt: string;
161
+ }
162
+ interface NewChatMessage {
163
+ conversationId: string;
164
+ role: ChatMessageRole;
165
+ content: string;
166
+ metadata?: string | null;
167
+ }
168
+ interface EFLink {
169
+ id: string;
170
+ userId: string;
171
+ name: string;
172
+ url: string;
173
+ icon: string | null;
174
+ sortOrder: number;
175
+ createdAt: string;
176
+ updatedAt: string;
177
+ }
178
+ interface NewEFLink {
179
+ name: string;
180
+ url: string;
181
+ icon?: string | null;
182
+ sortOrder?: number;
183
+ }
184
+ type LieuDayType = 'earned' | 'used';
185
+ interface LieuDay {
186
+ id: string;
187
+ userId: string;
188
+ type: LieuDayType;
189
+ date: string;
190
+ reason: string | null;
191
+ createdAt: string;
192
+ updatedAt: string;
193
+ }
194
+ interface NewLieuDay {
195
+ type: LieuDayType;
196
+ date: string;
197
+ reason?: string | null;
198
+ }
199
+ type BankConnectionStatus = 'pending' | 'linked' | 'expired' | 'error';
200
+ type TransactionType = 'debit' | 'credit';
201
+ interface BankConnection {
202
+ id: string;
203
+ userId: string;
204
+ provider: string;
205
+ institutionId: string;
206
+ institutionName: string;
207
+ requisitionId: string;
208
+ accountId: string | null;
209
+ status: BankConnectionStatus;
210
+ lastSynced: string | null;
211
+ createdAt: string;
212
+ updatedAt: string;
213
+ }
214
+ interface BankTransaction {
215
+ id: string;
216
+ connectionId: string | null;
217
+ externalId: string;
218
+ amount: number;
219
+ currency: string;
220
+ description: string | null;
221
+ merchantName: string | null;
222
+ bookingDate: string;
223
+ valueDate: string | null;
224
+ transactionType: TransactionType | null;
225
+ createdAt: string;
226
+ }
227
+ interface Setting {
228
+ id: string;
229
+ userId: string;
230
+ key: string;
231
+ value: string;
232
+ updatedAt: string;
233
+ }
234
+ interface ExchangeRate {
235
+ currency: string;
236
+ rateToCHF: number;
237
+ updatedAt: string;
238
+ }
239
+
240
+ export type { BankConnectionStatus as B, ChatMessageRole as C, ExpenseCategory as E, FocusCategory as F, IncomeCategory as I, LieuDayType as L, NewExpenseCategory as N, Setting as S, TransactionType as T, Expense as a, NewExpense as b, NewIncomeCategory as c, Income as d, NewIncome as e, NewFocusCategory as f, FocusSessionStatus as g, FocusSession as h, NewFocusSession as i, FocusSessionFilters as j, ChatConversation as k, NewChatConversation as l, ChatMessage as m, NewChatMessage as n, EFLink as o, NewEFLink as p, LieuDay as q, NewLieuDay as r, BankConnection as s, BankTransaction as t, ExchangeRate as u };
@@ -0,0 +1,4 @@
1
+ export { s as BankConnection, B as BankConnectionStatus, t as BankTransaction, k as ChatConversation, m as ChatMessage, C as ChatMessageRole, o as EFLink, u as ExchangeRate, a as Expense, E as ExpenseCategory, F as FocusCategory, h as FocusSession, j as FocusSessionFilters, g as FocusSessionStatus, d as Income, I as IncomeCategory, q as LieuDay, L as LieuDayType, l as NewChatConversation, n as NewChatMessage, p as NewEFLink, b as NewExpense, N as NewExpenseCategory, f as NewFocusCategory, i as NewFocusSession, e as NewIncome, c as NewIncomeCategory, r as NewLieuDay, S as Setting, T as TransactionType } from './database-BKc0Oj26.mjs';
2
+ export { ApiKeyInfo, CloudAgent, CloudAgentConversation, CloudAgentMessage, CloudAgentMessageType, CloudAgentModel, CloudAgentPrompt, CloudAgentSource, CloudAgentStatus, CloudAgentTarget, IssueQueryOptions, LINEAR_PRIORITY_LABELS, LaunchAgentRequest, LinearIssue, LinearPriority, LinearProject, LinearState, LinearStateType, LinearUser, ListAgentsResponse, ModelsResponse, PaginatedIssues } from './types/index.mjs';
3
+ export { API_URLS, CLOUD_AGENT_STATUSES, CLOUD_AGENT_STATUS_COLORS, CLOUD_AGENT_STATUS_EMOJI, CURRENCIES, CURRENCY_NAMES, CURRENCY_SYMBOLS, Currency, DEFAULT_FOCUS_DURATIONS, FOCUS_STATUS, FREQUENCIES, FREQUENCY_LABELS, FREQUENCY_MULTIPLIERS, FocusDuration, FocusStatus, Frequency, LINEAR_PRIORITIES, LINEAR_PRIORITY_COLORS, LinearPriorityValue, SETTING_KEYS, SettingKey } from './constants/index.mjs';
4
+ export { calculateFocusStats, calculateLieuBalance, calculateMonthlyExpenses, calculateMonthlyIncome, calculateMonthlySavings, calculateSavingsRate, formatCurrency, formatDate, formatDueDate, formatRelativeTime, formatTime, formatTotalTime, generateId, generateRandomColor, generateShortId, getRepoName, isNonEmptyString, isPositiveNumber, isValidCurrency, isValidEmail, isValidFrequency, isValidISODate, isValidUrl, toMonthlyAmount, toYearlyAmount, truncate } from './utils/index.mjs';
@@ -0,0 +1,4 @@
1
+ export { s as BankConnection, B as BankConnectionStatus, t as BankTransaction, k as ChatConversation, m as ChatMessage, C as ChatMessageRole, o as EFLink, u as ExchangeRate, a as Expense, E as ExpenseCategory, F as FocusCategory, h as FocusSession, j as FocusSessionFilters, g as FocusSessionStatus, d as Income, I as IncomeCategory, q as LieuDay, L as LieuDayType, l as NewChatConversation, n as NewChatMessage, p as NewEFLink, b as NewExpense, N as NewExpenseCategory, f as NewFocusCategory, i as NewFocusSession, e as NewIncome, c as NewIncomeCategory, r as NewLieuDay, S as Setting, T as TransactionType } from './database-BKc0Oj26.js';
2
+ export { ApiKeyInfo, CloudAgent, CloudAgentConversation, CloudAgentMessage, CloudAgentMessageType, CloudAgentModel, CloudAgentPrompt, CloudAgentSource, CloudAgentStatus, CloudAgentTarget, IssueQueryOptions, LINEAR_PRIORITY_LABELS, LaunchAgentRequest, LinearIssue, LinearPriority, LinearProject, LinearState, LinearStateType, LinearUser, ListAgentsResponse, ModelsResponse, PaginatedIssues } from './types/index.js';
3
+ export { API_URLS, CLOUD_AGENT_STATUSES, CLOUD_AGENT_STATUS_COLORS, CLOUD_AGENT_STATUS_EMOJI, CURRENCIES, CURRENCY_NAMES, CURRENCY_SYMBOLS, Currency, DEFAULT_FOCUS_DURATIONS, FOCUS_STATUS, FREQUENCIES, FREQUENCY_LABELS, FREQUENCY_MULTIPLIERS, FocusDuration, FocusStatus, Frequency, LINEAR_PRIORITIES, LINEAR_PRIORITY_COLORS, LinearPriorityValue, SETTING_KEYS, SettingKey } from './constants/index.js';
4
+ export { calculateFocusStats, calculateLieuBalance, calculateMonthlyExpenses, calculateMonthlyIncome, calculateMonthlySavings, calculateSavingsRate, formatCurrency, formatDate, formatDueDate, formatRelativeTime, formatTime, formatTotalTime, generateId, generateRandomColor, generateShortId, getRepoName, isNonEmptyString, isPositiveNumber, isValidCurrency, isValidEmail, isValidFrequency, isValidISODate, isValidUrl, toMonthlyAmount, toYearlyAmount, truncate } from './utils/index.js';
package/dist/index.js ADDED
@@ -0,0 +1,411 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var src_exports = {};
22
+ __export(src_exports, {
23
+ API_URLS: () => API_URLS,
24
+ CLOUD_AGENT_STATUSES: () => CLOUD_AGENT_STATUSES,
25
+ CLOUD_AGENT_STATUS_COLORS: () => CLOUD_AGENT_STATUS_COLORS,
26
+ CLOUD_AGENT_STATUS_EMOJI: () => CLOUD_AGENT_STATUS_EMOJI,
27
+ CURRENCIES: () => CURRENCIES,
28
+ CURRENCY_NAMES: () => CURRENCY_NAMES,
29
+ CURRENCY_SYMBOLS: () => CURRENCY_SYMBOLS,
30
+ DEFAULT_FOCUS_DURATIONS: () => DEFAULT_FOCUS_DURATIONS,
31
+ FOCUS_STATUS: () => FOCUS_STATUS,
32
+ FREQUENCIES: () => FREQUENCIES,
33
+ FREQUENCY_LABELS: () => FREQUENCY_LABELS,
34
+ FREQUENCY_MULTIPLIERS: () => FREQUENCY_MULTIPLIERS,
35
+ LINEAR_PRIORITIES: () => LINEAR_PRIORITIES,
36
+ LINEAR_PRIORITY_COLORS: () => LINEAR_PRIORITY_COLORS,
37
+ LINEAR_PRIORITY_LABELS: () => LINEAR_PRIORITY_LABELS,
38
+ SETTING_KEYS: () => SETTING_KEYS,
39
+ calculateFocusStats: () => calculateFocusStats,
40
+ calculateLieuBalance: () => calculateLieuBalance,
41
+ calculateMonthlyExpenses: () => calculateMonthlyExpenses,
42
+ calculateMonthlyIncome: () => calculateMonthlyIncome,
43
+ calculateMonthlySavings: () => calculateMonthlySavings,
44
+ calculateSavingsRate: () => calculateSavingsRate,
45
+ formatCurrency: () => formatCurrency,
46
+ formatDate: () => formatDate,
47
+ formatDueDate: () => formatDueDate,
48
+ formatRelativeTime: () => formatRelativeTime,
49
+ formatTime: () => formatTime,
50
+ formatTotalTime: () => formatTotalTime,
51
+ generateId: () => generateId,
52
+ generateRandomColor: () => generateRandomColor,
53
+ generateShortId: () => generateShortId,
54
+ getRepoName: () => getRepoName,
55
+ isNonEmptyString: () => isNonEmptyString,
56
+ isPositiveNumber: () => isPositiveNumber,
57
+ isValidCurrency: () => isValidCurrency,
58
+ isValidEmail: () => isValidEmail,
59
+ isValidFrequency: () => isValidFrequency,
60
+ isValidISODate: () => isValidISODate,
61
+ isValidUrl: () => isValidUrl,
62
+ toMonthlyAmount: () => toMonthlyAmount,
63
+ toYearlyAmount: () => toYearlyAmount,
64
+ truncate: () => truncate
65
+ });
66
+ module.exports = __toCommonJS(src_exports);
67
+
68
+ // src/types/linear.ts
69
+ var LINEAR_PRIORITY_LABELS = {
70
+ 0: "No priority",
71
+ 1: "Urgent",
72
+ 2: "High",
73
+ 3: "Medium",
74
+ 4: "Low"
75
+ };
76
+
77
+ // src/constants/index.ts
78
+ var CURRENCIES = ["CHF", "USD", "EUR", "PLN"];
79
+ var CURRENCY_SYMBOLS = {
80
+ CHF: "CHF",
81
+ USD: "$",
82
+ EUR: "\u20AC",
83
+ PLN: "z\u0142"
84
+ };
85
+ var CURRENCY_NAMES = {
86
+ CHF: "Swiss Franc",
87
+ USD: "US Dollar",
88
+ EUR: "Euro",
89
+ PLN: "Polish Z\u0142oty"
90
+ };
91
+ var FREQUENCIES = [
92
+ "monthly",
93
+ "yearly",
94
+ "6-monthly",
95
+ "weekly",
96
+ "one-time"
97
+ ];
98
+ var FREQUENCY_LABELS = {
99
+ monthly: "Monthly",
100
+ yearly: "Yearly",
101
+ "6-monthly": "Every 6 Months",
102
+ weekly: "Weekly",
103
+ "one-time": "One Time"
104
+ };
105
+ var FREQUENCY_MULTIPLIERS = {
106
+ monthly: 12,
107
+ yearly: 1,
108
+ "6-monthly": 2,
109
+ weekly: 52,
110
+ "one-time": 1
111
+ };
112
+ var DEFAULT_FOCUS_DURATIONS = [15, 25, 30, 45, 60, 90];
113
+ var FOCUS_STATUS = ["active", "completed", "abandoned"];
114
+ var LINEAR_PRIORITIES = [0, 1, 2, 3, 4];
115
+ var LINEAR_PRIORITY_COLORS = {
116
+ 0: "#6b7280",
117
+ // No priority - gray
118
+ 1: "#ef4444",
119
+ // Urgent - red
120
+ 2: "#f97316",
121
+ // High - orange
122
+ 3: "#eab308",
123
+ // Medium - yellow
124
+ 4: "#3b82f6"
125
+ // Low - blue
126
+ };
127
+ var CLOUD_AGENT_STATUSES = [
128
+ "CREATING",
129
+ "RUNNING",
130
+ "FINISHED",
131
+ "FAILED",
132
+ "CANCELLED"
133
+ ];
134
+ var CLOUD_AGENT_STATUS_EMOJI = {
135
+ CREATING: "\u{1F528}",
136
+ RUNNING: "\u23F3",
137
+ FINISHED: "\u2705",
138
+ FAILED: "\u274C",
139
+ CANCELLED: "\u{1F6AB}"
140
+ };
141
+ var CLOUD_AGENT_STATUS_COLORS = {
142
+ CREATING: "#3b82f6",
143
+ RUNNING: "#3b82f6",
144
+ FINISHED: "#10b981",
145
+ FAILED: "#ef4444",
146
+ CANCELLED: "#6b7280"
147
+ };
148
+ var API_URLS = {
149
+ CURSOR_CLOUD: "https://api.cursor.com",
150
+ LINEAR_GRAPHQL: "https://api.linear.app/graphql"
151
+ };
152
+ var SETTING_KEYS = {
153
+ // AI
154
+ AI_MODEL: "ai_model",
155
+ AI_REASONING_ENABLED: "ai_reasoning_enabled",
156
+ // API Keys
157
+ OPENAI_API_KEY: "openai_api_key",
158
+ ANTHROPIC_API_KEY: "anthropic_api_key",
159
+ CURSOR_API_KEY: "cursor_api_key",
160
+ LINEAR_API_KEY: "linear_api_key",
161
+ // Google
162
+ GOOGLE_CLIENT_ID: "google_client_id",
163
+ GOOGLE_CLIENT_SECRET: "google_client_secret",
164
+ GOOGLE_CALENDAR_TOKENS: "google_calendar_tokens",
165
+ SELECTED_CALENDAR_IDS: "selected_calendar_ids"
166
+ };
167
+
168
+ // src/utils/formatters.ts
169
+ function formatTime(seconds) {
170
+ const mins = Math.floor(seconds / 60);
171
+ const secs = seconds % 60;
172
+ return `${mins.toString().padStart(2, "0")}:${secs.toString().padStart(2, "0")}`;
173
+ }
174
+ function formatTotalTime(seconds) {
175
+ const hours = Math.floor(seconds / 3600);
176
+ const mins = Math.floor(seconds % 3600 / 60);
177
+ if (hours > 0) {
178
+ return `${hours}h ${mins}m`;
179
+ }
180
+ return `${mins}m`;
181
+ }
182
+ function formatCurrency(amount, currency, locale = "en-CH") {
183
+ return new Intl.NumberFormat(locale, {
184
+ style: "currency",
185
+ currency,
186
+ minimumFractionDigits: 2,
187
+ maximumFractionDigits: 2
188
+ }).format(amount);
189
+ }
190
+ function formatRelativeTime(dateStr) {
191
+ const date = new Date(dateStr);
192
+ const now = /* @__PURE__ */ new Date();
193
+ const diffMs = now.getTime() - date.getTime();
194
+ const diffMins = Math.floor(diffMs / 6e4);
195
+ const diffHours = Math.floor(diffMins / 60);
196
+ const diffDays = Math.floor(diffHours / 24);
197
+ if (diffMins < 1) return "Just now";
198
+ if (diffMins < 60) return `${diffMins}m ago`;
199
+ if (diffHours < 24) return `${diffHours}h ago`;
200
+ if (diffDays < 7) return `${diffDays}d ago`;
201
+ return date.toLocaleDateString();
202
+ }
203
+ function formatDate(dateStr, options = {
204
+ month: "short",
205
+ day: "numeric",
206
+ year: "numeric"
207
+ }) {
208
+ return new Date(dateStr).toLocaleDateString("en-US", options);
209
+ }
210
+ function formatDueDate(dueDate) {
211
+ if (!dueDate) return { text: "", isOverdue: false };
212
+ const due = new Date(dueDate);
213
+ const today = /* @__PURE__ */ new Date();
214
+ today.setHours(0, 0, 0, 0);
215
+ const tomorrow = new Date(today);
216
+ tomorrow.setDate(tomorrow.getDate() + 1);
217
+ const dueDay = new Date(due);
218
+ dueDay.setHours(0, 0, 0, 0);
219
+ const isOverdue = dueDay < today;
220
+ if (dueDay.getTime() === today.getTime()) {
221
+ return { text: "Today", isOverdue: false };
222
+ } else if (dueDay.getTime() === tomorrow.getTime()) {
223
+ return { text: "Tomorrow", isOverdue: false };
224
+ } else if (isOverdue) {
225
+ const daysAgo = Math.ceil(
226
+ (today.getTime() - dueDay.getTime()) / (1e3 * 60 * 60 * 24)
227
+ );
228
+ return { text: `${daysAgo}d overdue`, isOverdue: true };
229
+ } else {
230
+ return {
231
+ text: due.toLocaleDateString("en-US", { month: "short", day: "numeric" }),
232
+ isOverdue: false
233
+ };
234
+ }
235
+ }
236
+ function truncate(str, maxLength) {
237
+ if (str.length <= maxLength) return str;
238
+ return str.slice(0, maxLength) + "...";
239
+ }
240
+ function getRepoName(url) {
241
+ const match = url.match(/github\.com\/(.+)$/);
242
+ return match ? match[1] : url;
243
+ }
244
+
245
+ // src/utils/generators.ts
246
+ function generateId() {
247
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
248
+ return crypto.randomUUID();
249
+ }
250
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
251
+ const r = Math.random() * 16 | 0;
252
+ const v = c === "x" ? r : r & 3 | 8;
253
+ return v.toString(16);
254
+ });
255
+ }
256
+ function generateShortId() {
257
+ return `${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
258
+ }
259
+ function generateRandomColor() {
260
+ const colors = [
261
+ "#ef4444",
262
+ // red
263
+ "#f97316",
264
+ // orange
265
+ "#eab308",
266
+ // yellow
267
+ "#22c55e",
268
+ // green
269
+ "#14b8a6",
270
+ // teal
271
+ "#3b82f6",
272
+ // blue
273
+ "#8b5cf6",
274
+ // violet
275
+ "#ec4899"
276
+ // pink
277
+ ];
278
+ return colors[Math.floor(Math.random() * colors.length)];
279
+ }
280
+
281
+ // src/utils/validators.ts
282
+ function isValidEmail(email) {
283
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
284
+ return emailRegex.test(email);
285
+ }
286
+ function isValidUrl(url) {
287
+ try {
288
+ new URL(url);
289
+ return true;
290
+ } catch {
291
+ return false;
292
+ }
293
+ }
294
+ function isValidISODate(dateStr) {
295
+ const regex = /^\d{4}-\d{2}-\d{2}$/;
296
+ if (!regex.test(dateStr)) return false;
297
+ const date = new Date(dateStr);
298
+ return !isNaN(date.getTime());
299
+ }
300
+ function isValidCurrency(currency) {
301
+ const validCurrencies = ["CHF", "USD", "EUR", "PLN"];
302
+ return validCurrencies.includes(currency);
303
+ }
304
+ function isValidFrequency(frequency) {
305
+ const validFrequencies = ["monthly", "yearly", "6-monthly", "weekly", "one-time"];
306
+ return validFrequencies.includes(frequency);
307
+ }
308
+ function isPositiveNumber(value) {
309
+ return typeof value === "number" && !isNaN(value) && value > 0;
310
+ }
311
+ function isNonEmptyString(value) {
312
+ return typeof value === "string" && value.trim().length > 0;
313
+ }
314
+
315
+ // src/utils/calculations.ts
316
+ function toYearlyAmount(amount, frequency) {
317
+ const multiplier = FREQUENCY_MULTIPLIERS[frequency] || 1;
318
+ return amount * multiplier;
319
+ }
320
+ function toMonthlyAmount(amount, frequency) {
321
+ const yearly = toYearlyAmount(amount, frequency);
322
+ return yearly / 12;
323
+ }
324
+ function calculateMonthlyExpenses(expenses) {
325
+ return expenses.filter((e) => e.isActive).reduce((total, expense) => {
326
+ const monthly = toMonthlyAmount(
327
+ expense.amount,
328
+ expense.frequency
329
+ );
330
+ const myShare = monthly * expense.sharePercentage / 100;
331
+ return total + myShare;
332
+ }, 0);
333
+ }
334
+ function calculateMonthlyIncome(incomes) {
335
+ return incomes.filter((i) => i.isActive).reduce((total, income) => {
336
+ const monthly = toMonthlyAmount(income.amount, income.frequency);
337
+ return total + monthly;
338
+ }, 0);
339
+ }
340
+ function calculateMonthlySavings(incomes, expenses) {
341
+ return calculateMonthlyIncome(incomes) - calculateMonthlyExpenses(expenses);
342
+ }
343
+ function calculateSavingsRate(incomes, expenses) {
344
+ const income = calculateMonthlyIncome(incomes);
345
+ if (income === 0) return 0;
346
+ const savings = calculateMonthlySavings(incomes, expenses);
347
+ return savings / income * 100;
348
+ }
349
+ function calculateLieuBalance(lieuDays) {
350
+ return lieuDays.reduce((balance, day) => {
351
+ return day.type === "earned" ? balance + 1 : balance - 1;
352
+ }, 0);
353
+ }
354
+ function calculateFocusStats(sessions) {
355
+ const completed = sessions.filter((s) => s.status === "completed");
356
+ const abandoned = sessions.filter((s) => s.status === "abandoned");
357
+ const totalSeconds = completed.reduce((sum, s) => sum + s.actualSeconds, 0);
358
+ const completionRate = sessions.length > 0 ? completed.length / sessions.length * 100 : 0;
359
+ return {
360
+ totalSeconds,
361
+ completedCount: completed.length,
362
+ abandonedCount: abandoned.length,
363
+ completionRate
364
+ };
365
+ }
366
+ // Annotate the CommonJS export names for ESM import in node:
367
+ 0 && (module.exports = {
368
+ API_URLS,
369
+ CLOUD_AGENT_STATUSES,
370
+ CLOUD_AGENT_STATUS_COLORS,
371
+ CLOUD_AGENT_STATUS_EMOJI,
372
+ CURRENCIES,
373
+ CURRENCY_NAMES,
374
+ CURRENCY_SYMBOLS,
375
+ DEFAULT_FOCUS_DURATIONS,
376
+ FOCUS_STATUS,
377
+ FREQUENCIES,
378
+ FREQUENCY_LABELS,
379
+ FREQUENCY_MULTIPLIERS,
380
+ LINEAR_PRIORITIES,
381
+ LINEAR_PRIORITY_COLORS,
382
+ LINEAR_PRIORITY_LABELS,
383
+ SETTING_KEYS,
384
+ calculateFocusStats,
385
+ calculateLieuBalance,
386
+ calculateMonthlyExpenses,
387
+ calculateMonthlyIncome,
388
+ calculateMonthlySavings,
389
+ calculateSavingsRate,
390
+ formatCurrency,
391
+ formatDate,
392
+ formatDueDate,
393
+ formatRelativeTime,
394
+ formatTime,
395
+ formatTotalTime,
396
+ generateId,
397
+ generateRandomColor,
398
+ generateShortId,
399
+ getRepoName,
400
+ isNonEmptyString,
401
+ isPositiveNumber,
402
+ isValidCurrency,
403
+ isValidEmail,
404
+ isValidFrequency,
405
+ isValidISODate,
406
+ isValidUrl,
407
+ toMonthlyAmount,
408
+ toYearlyAmount,
409
+ truncate
410
+ });
411
+ //# sourceMappingURL=index.js.map