Consolidate server utilities into shared module
- Add lib/server/utils/normalize.ts with normalizeTicker, normalizeTagsOrNull, nowIso, todayIso - Add lib/server/utils/validation.ts with asRecord, asBoolean, asStringArray, asEnum - Add lib/server/utils/index.ts re-exporting all utilities - Remove duplicate lib/server/utils.ts (old file) - Update all repos and files to use shared utilities - Remove redundant ?? '' from normalizeTicker calls - Update watchlist.ts to use normalizeTagsOrNull for null-return tags
This commit is contained in:
@@ -6,38 +6,12 @@ import type {
|
||||
} from '@/lib/types';
|
||||
import { db } from '@/lib/server/db';
|
||||
import { watchlistItem } from '@/lib/server/db/schema';
|
||||
import { normalizeTicker, normalizeTagsOrNull, nowIso } from '@/lib/server/utils';
|
||||
|
||||
type WatchlistRow = typeof watchlistItem.$inferSelect;
|
||||
const DEFAULT_STATUS: CoverageStatus = 'backlog';
|
||||
const DEFAULT_PRIORITY: CoveragePriority = 'medium';
|
||||
|
||||
function normalizeTags(tags?: string[]) {
|
||||
if (!Array.isArray(tags)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const unique = new Set<string>();
|
||||
|
||||
for (const entry of tags) {
|
||||
if (typeof entry !== 'string') {
|
||||
continue;
|
||||
}
|
||||
|
||||
const tag = entry.trim();
|
||||
if (!tag) {
|
||||
continue;
|
||||
}
|
||||
|
||||
unique.add(tag);
|
||||
}
|
||||
|
||||
if (unique.size === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return [...unique];
|
||||
}
|
||||
|
||||
function toWatchlistItem(row: WatchlistRow, latestFilingDate: string | null = null): WatchlistItem {
|
||||
return {
|
||||
id: row.id,
|
||||
@@ -79,7 +53,7 @@ export async function getWatchlistItemById(userId: string, id: number) {
|
||||
}
|
||||
|
||||
export async function getWatchlistItemByTicker(userId: string, ticker: string) {
|
||||
const normalizedTicker = ticker.trim().toUpperCase();
|
||||
const normalizedTicker = normalizeTicker(ticker);
|
||||
if (!normalizedTicker) {
|
||||
return null;
|
||||
}
|
||||
@@ -104,15 +78,15 @@ export async function upsertWatchlistItemRecord(input: {
|
||||
priority?: CoveragePriority;
|
||||
lastReviewedAt?: string | null;
|
||||
}) {
|
||||
const normalizedTicker = input.ticker.trim().toUpperCase();
|
||||
const normalizedTicker = normalizeTicker(input.ticker);
|
||||
const normalizedSector = input.sector?.trim() ? input.sector.trim() : null;
|
||||
const normalizedCategory = input.category?.trim() ? input.category.trim() : null;
|
||||
const normalizedTags = normalizeTags(input.tags);
|
||||
const normalizedTags = normalizeTagsOrNull(input.tags);
|
||||
const normalizedCompanyName = input.companyName.trim();
|
||||
const normalizedLastReviewedAt = input.lastReviewedAt?.trim()
|
||||
? input.lastReviewedAt.trim()
|
||||
: null;
|
||||
const now = new Date().toISOString();
|
||||
const timestamp = nowIso();
|
||||
|
||||
const [inserted] = await db
|
||||
.insert(watchlistItem)
|
||||
@@ -125,8 +99,8 @@ export async function upsertWatchlistItemRecord(input: {
|
||||
tags: normalizedTags,
|
||||
status: input.status ?? DEFAULT_STATUS,
|
||||
priority: input.priority ?? DEFAULT_PRIORITY,
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
created_at: timestamp,
|
||||
updated_at: timestamp,
|
||||
last_reviewed_at: normalizedLastReviewedAt
|
||||
})
|
||||
.onConflictDoNothing({
|
||||
@@ -156,7 +130,7 @@ export async function upsertWatchlistItemRecord(input: {
|
||||
tags: normalizedTags,
|
||||
status: input.status ?? existing?.status ?? DEFAULT_STATUS,
|
||||
priority: input.priority ?? existing?.priority ?? DEFAULT_PRIORITY,
|
||||
updated_at: now,
|
||||
updated_at: timestamp,
|
||||
last_reviewed_at: normalizedLastReviewedAt ?? existing?.last_reviewed_at ?? null
|
||||
})
|
||||
.where(and(eq(watchlistItem.user_id, input.userId), eq(watchlistItem.ticker, normalizedTicker)))
|
||||
@@ -212,7 +186,7 @@ export async function updateWatchlistItemRecord(input: {
|
||||
: null;
|
||||
const nextTags = input.tags === undefined
|
||||
? existing.tags ?? null
|
||||
: normalizeTags(input.tags);
|
||||
: normalizeTagsOrNull(input.tags);
|
||||
const nextLastReviewedAt = input.lastReviewedAt === undefined
|
||||
? existing.last_reviewed_at
|
||||
: input.lastReviewedAt?.trim()
|
||||
@@ -228,7 +202,7 @@ export async function updateWatchlistItemRecord(input: {
|
||||
tags: nextTags,
|
||||
status: input.status ?? existing.status ?? DEFAULT_STATUS,
|
||||
priority: input.priority ?? existing.priority ?? DEFAULT_PRIORITY,
|
||||
updated_at: new Date().toISOString(),
|
||||
updated_at: nowIso(),
|
||||
last_reviewed_at: nextLastReviewedAt
|
||||
})
|
||||
.where(and(eq(watchlistItem.user_id, input.userId), eq(watchlistItem.id, input.id)))
|
||||
@@ -242,7 +216,7 @@ export async function updateWatchlistReviewByTicker(
|
||||
ticker: string,
|
||||
reviewedAt: string
|
||||
) {
|
||||
const normalizedTicker = ticker.trim().toUpperCase();
|
||||
const normalizedTicker = normalizeTicker(ticker);
|
||||
if (!normalizedTicker) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user