From 3f9a6cf23e689b141faa182df6cd4e0b3accea33 Mon Sep 17 00:00:00 2001 From: null Date: Sun, 5 Jul 2026 13:48:44 -0500 Subject: [PATCH] =?UTF-8?q?feat(server-ts):=20utils/dates=20=E2=86=92=20Ty?= =?UTF-8?q?peScript=20(.mts);=20utils/=20dir=20now=20TS=20(Phase=202)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit dates.mts (ESM, typed) joins money.mts — the two most-required server leaves are now TypeScript. Added .cts to the server tsconfig for the CJS-module migration path. typecheck:server clean; suite 225 + probe 17/17. Co-Authored-By: Claude Opus 4.8 --- routes/bills.js | 2 +- routes/calendar.js | 2 +- routes/matches.js | 2 +- routes/payments.js | 2 +- routes/status.js | 2 +- routes/transactions.js | 2 +- services/billMerchantRuleService.js | 2 +- services/billsService.js | 2 +- services/driftService.js | 2 +- services/notificationService.js | 2 +- services/spendingService.js | 2 +- services/subscriptionService.js | 2 +- services/trackerService.js | 2 +- tsconfig.server.json | 14 +++++++------- utils/{dates.js => dates.mts} | 16 ++++++---------- workers/dailyWorker.js | 2 +- 16 files changed, 27 insertions(+), 31 deletions(-) rename utils/{dates.js => dates.mts} (72%) diff --git a/routes/bills.js b/routes/bills.js index a560054..cd83cb9 100644 --- a/routes/bills.js +++ b/routes/bills.js @@ -22,7 +22,7 @@ const { accountingActiveSql, applyBankPaymentAsSourceOfTruth, } = require('../services/paymentAccountingService'); -const { localDateString, todayLocal } = require('../utils/dates'); +const { localDateString, todayLocal } = require('../utils/dates.mts'); const { roundMoney, sumMoney, toCents, fromCents } = require('../utils/money.mts'); // ── GET /api/bills ──────────────────────────────────────────────────────────── diff --git a/routes/calendar.js b/routes/calendar.js index fcd14bb..a29f903 100644 --- a/routes/calendar.js +++ b/routes/calendar.js @@ -13,7 +13,7 @@ const { regenerateToken, revokeToken, } = require('../services/calendarFeedService'); -const { localDateString } = require('../utils/dates'); +const { localDateString } = require('../utils/dates.mts'); const { roundMoney, sumMoney, fromCents } = require('../utils/money.mts'); function clampDay(year, month, day) { diff --git a/routes/matches.js b/routes/matches.js index d3f5cd1..3b5f830 100644 --- a/routes/matches.js +++ b/routes/matches.js @@ -9,7 +9,7 @@ const { const { learnMerchantRuleFromMatch } = require('../services/billMerchantRuleService'); const { markMatched, markUnmatched } = require('../services/transactionMatchState'); const { serializePayment } = require('../services/paymentValidation'); -const { todayLocal } = require('../utils/dates'); +const { todayLocal } = require('../utils/dates.mts'); function sendMatchError(res, err, fallbackMessage = 'Match operation failed') { if (err.status) { diff --git a/routes/payments.js b/routes/payments.js index 59dbbe4..3a20d41 100644 --- a/routes/payments.js +++ b/routes/payments.js @@ -10,7 +10,7 @@ const { markProvisionalManualPaymentsOverridden, reactivatePaymentsOverriddenBy, } = require('../services/paymentAccountingService'); -const { todayLocal } = require('../utils/dates'); +const { todayLocal } = require('../utils/dates.mts'); const { fromCents } = require('../utils/money.mts'); // SQL_NOT_DELETED is a compile-time constant SQL fragment, never user-supplied. diff --git a/routes/status.js b/routes/status.js index 667ea4f..0652f2f 100644 --- a/routes/status.js +++ b/routes/status.js @@ -10,7 +10,7 @@ const { checkForUpdates } = require('../services/updateCheckService'); const { getStatus: getBankSyncWorkerStatus } = require('../services/bankSyncWorker'); const { getBankSyncConfig } = require('../services/bankSyncConfigService'); const { accountingActiveSql } = require('../services/paymentAccountingService'); -const { localDateString } = require('../utils/dates'); +const { localDateString } = require('../utils/dates.mts'); const startTime = Date.now(); let pkg; diff --git a/routes/transactions.js b/routes/transactions.js index f3fdd3d..4a6f43f 100644 --- a/routes/transactions.js +++ b/routes/transactions.js @@ -22,7 +22,7 @@ const { findOrCreateCategory, } = require('../services/merchantStoreMatchService'); const { categorizeTransaction } = require('../services/spendingService'); -const { todayLocal } = require('../utils/dates'); +const { todayLocal } = require('../utils/dates.mts'); const { roundMoney } = require('../utils/money.mts'); const MATCH_STATUSES = new Set(['unmatched', 'matched', 'ignored']); diff --git a/services/billMerchantRuleService.js b/services/billMerchantRuleService.js index 867362b..6bb8d0d 100644 --- a/services/billMerchantRuleService.js +++ b/services/billMerchantRuleService.js @@ -3,7 +3,7 @@ const { normalizeMerchant } = require('./subscriptionService'); const { getUserSettings } = require('./userSettings'); const { applyBankPaymentAsSourceOfTruth } = require('./paymentAccountingService'); -const { localDateString } = require('../utils/dates'); +const { localDateString } = require('../utils/dates.mts'); const { fromCents } = require('../utils/money.mts'); // Word-boundary merchant match — requires the rule to appear as complete word(s) diff --git a/services/billsService.js b/services/billsService.js index 8939bee..a36c1bc 100644 --- a/services/billsService.js +++ b/services/billsService.js @@ -1,4 +1,4 @@ -const { monthKey } = require('../utils/dates'); +const { monthKey } = require('../utils/dates.mts'); const { toCents, fromCents } = require('../utils/money.mts'); const VALID_VISIBILITY = ['default', 'all', 'ranges', 'none']; diff --git a/services/driftService.js b/services/driftService.js index 5721f94..62a3638 100644 --- a/services/driftService.js +++ b/services/driftService.js @@ -4,7 +4,7 @@ const { getDb } = require('../db/database'); const { getCycleRange } = require('./statusService'); const { accountingActiveSql } = require('./paymentAccountingService'); const { getUserSettings } = require('./userSettings'); -const { localDateString } = require('../utils/dates'); +const { localDateString } = require('../utils/dates.mts'); const { roundMoney, fromCents } = require('../utils/money.mts'); const MONTHS_BACK = 3; diff --git a/services/notificationService.js b/services/notificationService.js index 00e77ec..b367c32 100644 --- a/services/notificationService.js +++ b/services/notificationService.js @@ -7,7 +7,7 @@ const { markNotificationSuccess, markNotificationTestSuccess, } = require('./statusRuntime'); -const { localDateString } = require('../utils/dates'); +const { localDateString } = require('../utils/dates.mts'); const { fromCents } = require('../utils/money.mts'); // ── Push notification channels ──────────────────────────────────────────────── diff --git a/services/spendingService.js b/services/spendingService.js index b5ce17b..4460edd 100644 --- a/services/spendingService.js +++ b/services/spendingService.js @@ -1,7 +1,7 @@ 'use strict'; const { normalizeMerchant } = require('./subscriptionService'); -const { localDateString } = require('../utils/dates'); +const { localDateString } = require('../utils/dates.mts'); const { toCents, fromCents } = require('../utils/money.mts'); // Spending = unmatched outflows (amount < 0) that haven't been ignored. diff --git a/services/subscriptionService.js b/services/subscriptionService.js index 1b38af2..e8853c8 100644 --- a/services/subscriptionService.js +++ b/services/subscriptionService.js @@ -1,7 +1,7 @@ 'use strict'; const { billingCycleForCycleType, insertBill, validateBillData } = require('./billsService'); -const { localDateString, todayLocal } = require('../utils/dates'); +const { localDateString, todayLocal } = require('../utils/dates.mts'); const { roundMoney, sumMoney, mulMoney, fromCents } = require('../utils/money.mts'); const SUBSCRIPTION_TYPES = [ diff --git a/services/trackerService.js b/services/trackerService.js index 86d7109..83e8ad1 100644 --- a/services/trackerService.js +++ b/services/trackerService.js @@ -7,7 +7,7 @@ const { computeBalanceDelta, applyBalanceDelta } = require('./billsService'); const { computeAmountSuggestionsBatch } = require('./amountSuggestionService'); const { accountingActiveSql } = require('./paymentAccountingService'); const { normalizeMerchant } = require('./subscriptionService'); -const { localDateString } = require('../utils/dates'); +const { localDateString } = require('../utils/dates.mts'); const { sumMoney, roundMoney, fromCents } = require('../utils/money.mts'); const DEFAULT_PENDING_DAYS = 3; diff --git a/tsconfig.server.json b/tsconfig.server.json index d32f557..ee2f1bd 100644 --- a/tsconfig.server.json +++ b/tsconfig.server.json @@ -16,12 +16,12 @@ "types": ["node"] }, "include": [ - "utils/**/*.ts", "utils/**/*.mts", - "services/**/*.ts", "services/**/*.mts", - "routes/**/*.ts", "routes/**/*.mts", - "middleware/**/*.ts", "middleware/**/*.mts", - "db/**/*.ts", "db/**/*.mts", - "workers/**/*.ts", "workers/**/*.mts", - "server.mts" + "utils/**/*.ts", "utils/**/*.mts", "utils/**/*.cts", + "services/**/*.ts", "services/**/*.mts", "services/**/*.cts", + "routes/**/*.ts", "routes/**/*.mts", "routes/**/*.cts", + "middleware/**/*.ts", "middleware/**/*.mts", "middleware/**/*.cts", + "db/**/*.ts", "db/**/*.mts", "db/**/*.cts", + "workers/**/*.ts", "workers/**/*.mts", "workers/**/*.cts", + "server.mts", "server.cts" ] } diff --git a/utils/dates.js b/utils/dates.mts similarity index 72% rename from utils/dates.js rename to utils/dates.mts index f3078a8..1c0e556 100644 --- a/utils/dates.js +++ b/utils/dates.mts @@ -1,17 +1,15 @@ -'use strict'; - /** * Server-side local-date helpers. * * Bill due dates and payment dates are stored as plain YYYY-MM-DD strings in * the server's local timezone (the client computes "today" the same way in - * client/pages/TrackerPage.jsx → localDateString). Never derive a calendar + * client/pages/TrackerPage.tsx → localDateString). Never derive a calendar * date from Date.toISOString() — that yields the UTC date, which disagrees * with local time around midnight and month boundaries. */ /** YYYY-MM-DD in local time. */ -function localDateString(date = new Date()) { +export function localDateString(date: Date = new Date()): string { const year = date.getFullYear(); const month = String(date.getMonth() + 1).padStart(2, '0'); const day = String(date.getDate()).padStart(2, '0'); @@ -19,25 +17,23 @@ function localDateString(date = new Date()) { } /** { year, month } (1-based month) in local time. */ -function localYearMonth(date = new Date()) { +export function localYearMonth(date: Date = new Date()): { year: number; month: number } { return { year: date.getFullYear(), month: date.getMonth() + 1 }; } /** YYYY-MM-DD in local time, `days` days before `from`. */ -function localDateStringDaysAgo(days, from = new Date()) { +export function localDateStringDaysAgo(days: number, from: Date = new Date()): string { const d = new Date(from); d.setDate(d.getDate() - days); return localDateString(d); } /** Today as YYYY-MM-DD in local time. Alias of localDateString(). */ -function todayLocal() { +export function todayLocal(): string { return localDateString(new Date()); } /** YYYY-MM month key in local time (e.g. "2026-06"). */ -function monthKey(date = new Date()) { +export function monthKey(date: Date = new Date()): string { return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`; } - -module.exports = { localDateString, localYearMonth, localDateStringDaysAgo, todayLocal, monthKey }; diff --git a/workers/dailyWorker.js b/workers/dailyWorker.js index c9dd05c..b205f5f 100644 --- a/workers/dailyWorker.js +++ b/workers/dailyWorker.js @@ -12,7 +12,7 @@ const { markWorkerStarted, markWorkerSuccess, } = require('../services/statusRuntime'); -const { localDateString, localDateStringDaysAgo } = require('../utils/dates'); +const { localDateString, localDateStringDaysAgo } = require('../utils/dates.mts'); const DAILY_CRON_HOUR = 6;