feat(server-ts): utils/dates → TypeScript (.mts); utils/ dir now TS (Phase 2)

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 <noreply@anthropic.com>
This commit is contained in:
null 2026-07-05 13:48:44 -05:00
parent 200c6373f5
commit 3f9a6cf23e
16 changed files with 27 additions and 31 deletions

View File

@ -22,7 +22,7 @@ const {
accountingActiveSql, accountingActiveSql,
applyBankPaymentAsSourceOfTruth, applyBankPaymentAsSourceOfTruth,
} = require('../services/paymentAccountingService'); } = 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'); const { roundMoney, sumMoney, toCents, fromCents } = require('../utils/money.mts');
// ── GET /api/bills ──────────────────────────────────────────────────────────── // ── GET /api/bills ────────────────────────────────────────────────────────────

View File

@ -13,7 +13,7 @@ const {
regenerateToken, regenerateToken,
revokeToken, revokeToken,
} = require('../services/calendarFeedService'); } = require('../services/calendarFeedService');
const { localDateString } = require('../utils/dates'); const { localDateString } = require('../utils/dates.mts');
const { roundMoney, sumMoney, fromCents } = require('../utils/money.mts'); const { roundMoney, sumMoney, fromCents } = require('../utils/money.mts');
function clampDay(year, month, day) { function clampDay(year, month, day) {

View File

@ -9,7 +9,7 @@ const {
const { learnMerchantRuleFromMatch } = require('../services/billMerchantRuleService'); const { learnMerchantRuleFromMatch } = require('../services/billMerchantRuleService');
const { markMatched, markUnmatched } = require('../services/transactionMatchState'); const { markMatched, markUnmatched } = require('../services/transactionMatchState');
const { serializePayment } = require('../services/paymentValidation'); const { serializePayment } = require('../services/paymentValidation');
const { todayLocal } = require('../utils/dates'); const { todayLocal } = require('../utils/dates.mts');
function sendMatchError(res, err, fallbackMessage = 'Match operation failed') { function sendMatchError(res, err, fallbackMessage = 'Match operation failed') {
if (err.status) { if (err.status) {

View File

@ -10,7 +10,7 @@ const {
markProvisionalManualPaymentsOverridden, markProvisionalManualPaymentsOverridden,
reactivatePaymentsOverriddenBy, reactivatePaymentsOverriddenBy,
} = require('../services/paymentAccountingService'); } = require('../services/paymentAccountingService');
const { todayLocal } = require('../utils/dates'); const { todayLocal } = require('../utils/dates.mts');
const { fromCents } = require('../utils/money.mts'); const { fromCents } = require('../utils/money.mts');
// SQL_NOT_DELETED is a compile-time constant SQL fragment, never user-supplied. // SQL_NOT_DELETED is a compile-time constant SQL fragment, never user-supplied.

View File

@ -10,7 +10,7 @@ const { checkForUpdates } = require('../services/updateCheckService');
const { getStatus: getBankSyncWorkerStatus } = require('../services/bankSyncWorker'); const { getStatus: getBankSyncWorkerStatus } = require('../services/bankSyncWorker');
const { getBankSyncConfig } = require('../services/bankSyncConfigService'); const { getBankSyncConfig } = require('../services/bankSyncConfigService');
const { accountingActiveSql } = require('../services/paymentAccountingService'); const { accountingActiveSql } = require('../services/paymentAccountingService');
const { localDateString } = require('../utils/dates'); const { localDateString } = require('../utils/dates.mts');
const startTime = Date.now(); const startTime = Date.now();
let pkg; let pkg;

View File

@ -22,7 +22,7 @@ const {
findOrCreateCategory, findOrCreateCategory,
} = require('../services/merchantStoreMatchService'); } = require('../services/merchantStoreMatchService');
const { categorizeTransaction } = require('../services/spendingService'); const { categorizeTransaction } = require('../services/spendingService');
const { todayLocal } = require('../utils/dates'); const { todayLocal } = require('../utils/dates.mts');
const { roundMoney } = require('../utils/money.mts'); const { roundMoney } = require('../utils/money.mts');
const MATCH_STATUSES = new Set(['unmatched', 'matched', 'ignored']); const MATCH_STATUSES = new Set(['unmatched', 'matched', 'ignored']);

View File

@ -3,7 +3,7 @@
const { normalizeMerchant } = require('./subscriptionService'); const { normalizeMerchant } = require('./subscriptionService');
const { getUserSettings } = require('./userSettings'); const { getUserSettings } = require('./userSettings');
const { applyBankPaymentAsSourceOfTruth } = require('./paymentAccountingService'); const { applyBankPaymentAsSourceOfTruth } = require('./paymentAccountingService');
const { localDateString } = require('../utils/dates'); const { localDateString } = require('../utils/dates.mts');
const { fromCents } = require('../utils/money.mts'); const { fromCents } = require('../utils/money.mts');
// Word-boundary merchant match — requires the rule to appear as complete word(s) // Word-boundary merchant match — requires the rule to appear as complete word(s)

View File

@ -1,4 +1,4 @@
const { monthKey } = require('../utils/dates'); const { monthKey } = require('../utils/dates.mts');
const { toCents, fromCents } = require('../utils/money.mts'); const { toCents, fromCents } = require('../utils/money.mts');
const VALID_VISIBILITY = ['default', 'all', 'ranges', 'none']; const VALID_VISIBILITY = ['default', 'all', 'ranges', 'none'];

View File

@ -4,7 +4,7 @@ const { getDb } = require('../db/database');
const { getCycleRange } = require('./statusService'); const { getCycleRange } = require('./statusService');
const { accountingActiveSql } = require('./paymentAccountingService'); const { accountingActiveSql } = require('./paymentAccountingService');
const { getUserSettings } = require('./userSettings'); const { getUserSettings } = require('./userSettings');
const { localDateString } = require('../utils/dates'); const { localDateString } = require('../utils/dates.mts');
const { roundMoney, fromCents } = require('../utils/money.mts'); const { roundMoney, fromCents } = require('../utils/money.mts');
const MONTHS_BACK = 3; const MONTHS_BACK = 3;

View File

@ -7,7 +7,7 @@ const {
markNotificationSuccess, markNotificationSuccess,
markNotificationTestSuccess, markNotificationTestSuccess,
} = require('./statusRuntime'); } = require('./statusRuntime');
const { localDateString } = require('../utils/dates'); const { localDateString } = require('../utils/dates.mts');
const { fromCents } = require('../utils/money.mts'); const { fromCents } = require('../utils/money.mts');
// ── Push notification channels ──────────────────────────────────────────────── // ── Push notification channels ────────────────────────────────────────────────

View File

@ -1,7 +1,7 @@
'use strict'; 'use strict';
const { normalizeMerchant } = require('./subscriptionService'); const { normalizeMerchant } = require('./subscriptionService');
const { localDateString } = require('../utils/dates'); const { localDateString } = require('../utils/dates.mts');
const { toCents, fromCents } = require('../utils/money.mts'); const { toCents, fromCents } = require('../utils/money.mts');
// Spending = unmatched outflows (amount < 0) that haven't been ignored. // Spending = unmatched outflows (amount < 0) that haven't been ignored.

View File

@ -1,7 +1,7 @@
'use strict'; 'use strict';
const { billingCycleForCycleType, insertBill, validateBillData } = require('./billsService'); 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 { roundMoney, sumMoney, mulMoney, fromCents } = require('../utils/money.mts');
const SUBSCRIPTION_TYPES = [ const SUBSCRIPTION_TYPES = [

View File

@ -7,7 +7,7 @@ const { computeBalanceDelta, applyBalanceDelta } = require('./billsService');
const { computeAmountSuggestionsBatch } = require('./amountSuggestionService'); const { computeAmountSuggestionsBatch } = require('./amountSuggestionService');
const { accountingActiveSql } = require('./paymentAccountingService'); const { accountingActiveSql } = require('./paymentAccountingService');
const { normalizeMerchant } = require('./subscriptionService'); const { normalizeMerchant } = require('./subscriptionService');
const { localDateString } = require('../utils/dates'); const { localDateString } = require('../utils/dates.mts');
const { sumMoney, roundMoney, fromCents } = require('../utils/money.mts'); const { sumMoney, roundMoney, fromCents } = require('../utils/money.mts');
const DEFAULT_PENDING_DAYS = 3; const DEFAULT_PENDING_DAYS = 3;

View File

@ -16,12 +16,12 @@
"types": ["node"] "types": ["node"]
}, },
"include": [ "include": [
"utils/**/*.ts", "utils/**/*.mts", "utils/**/*.ts", "utils/**/*.mts", "utils/**/*.cts",
"services/**/*.ts", "services/**/*.mts", "services/**/*.ts", "services/**/*.mts", "services/**/*.cts",
"routes/**/*.ts", "routes/**/*.mts", "routes/**/*.ts", "routes/**/*.mts", "routes/**/*.cts",
"middleware/**/*.ts", "middleware/**/*.mts", "middleware/**/*.ts", "middleware/**/*.mts", "middleware/**/*.cts",
"db/**/*.ts", "db/**/*.mts", "db/**/*.ts", "db/**/*.mts", "db/**/*.cts",
"workers/**/*.ts", "workers/**/*.mts", "workers/**/*.ts", "workers/**/*.mts", "workers/**/*.cts",
"server.mts" "server.mts", "server.cts"
] ]
} }

View File

@ -1,17 +1,15 @@
'use strict';
/** /**
* Server-side local-date helpers. * Server-side local-date helpers.
* *
* Bill due dates and payment dates are stored as plain YYYY-MM-DD strings in * 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 * 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 * date from Date.toISOString() that yields the UTC date, which disagrees
* with local time around midnight and month boundaries. * with local time around midnight and month boundaries.
*/ */
/** YYYY-MM-DD in local time. */ /** YYYY-MM-DD in local time. */
function localDateString(date = new Date()) { export function localDateString(date: Date = new Date()): string {
const year = date.getFullYear(); const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0'); const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).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. */ /** { 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 }; return { year: date.getFullYear(), month: date.getMonth() + 1 };
} }
/** YYYY-MM-DD in local time, `days` days before `from`. */ /** 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); const d = new Date(from);
d.setDate(d.getDate() - days); d.setDate(d.getDate() - days);
return localDateString(d); return localDateString(d);
} }
/** Today as YYYY-MM-DD in local time. Alias of localDateString(). */ /** Today as YYYY-MM-DD in local time. Alias of localDateString(). */
function todayLocal() { export function todayLocal(): string {
return localDateString(new Date()); return localDateString(new Date());
} }
/** YYYY-MM month key in local time (e.g. "2026-06"). */ /** 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')}`; return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`;
} }
module.exports = { localDateString, localYearMonth, localDateStringDaysAgo, todayLocal, monthKey };

View File

@ -12,7 +12,7 @@ const {
markWorkerStarted, markWorkerStarted,
markWorkerSuccess, markWorkerSuccess,
} = require('../services/statusRuntime'); } = require('../services/statusRuntime');
const { localDateString, localDateStringDaysAgo } = require('../utils/dates'); const { localDateString, localDateStringDaysAgo } = require('../utils/dates.mts');
const DAILY_CRON_HOUR = 6; const DAILY_CRON_HOUR = 6;