Commit Graph

36 Commits

Author SHA1 Message Date
null af20d8de07 fix(deps)!: clear both high-severity vulns; CI audit gate raised to high
- nodemailer 8 -> 9 (GHSA-p6gq-j5cr-w38f, raw-option file-read/SSRF; we
  never use the raw option — notificationDelivery tests 13/13)
- xlsx -> official SheetJS dist 0.20.3 (prototype pollution
  GHSA-4r6h-8v6p-xvw6 + ReDoS GHSA-5pgg-2g8v-p4x9; npm registry line is
  abandoned at 0.18.5 with no fix). API-identical for our 8 call sites;
  import/export round-trip tests green. NOTE: the lockfile now pins
  https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz — CI runners and
  Docker builds need egress to cdn.sheetjs.com
- CI: npm audit --audit-level critical -> high (0 vulns as of this
  commit; highs can no longer pass silently — QA-B0-02)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 17:34:51 -05:00
null 94ed0b63f3 chore(deps): semver-safe update sweep (4a)
npm update across the Wanted column: Radix x11, react-query 5.101,
better-sqlite3 12.11, node-cron 4.6, express-rate-limit, framer-motion,
postcss/autoprefixer, fast-check, knip, lefthook, typescript-eslint,
vitest 4.1.10, @types/*. Full ci green (lint 0 errors, server 252,
client 52, build + PWA emit).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 17:33:34 -05:00
null 35a0e4262e feat(db): Kysely typed-SQL POC — sync builder, proven parity (Pillar E)
De-risks the flagship. The app is synchronous (better-sqlite3) but Kysely
executes async, so db/kysely.cts uses Kysely as a TYPE-SAFE BUILDER only:
build the query (columns + user_id/deleted_at scoping + types checked at
compile time), `.compile()`, then run via sync helpers (all/get/run) on the
shared connection. No async ripple, no change to the sync model.

- Schema typed for `categories` (extend as adopted).
- tests/kyselyPoc.test.js proves byte-identical rows vs the raw SQL and
  that compiled SQL is parameterized (injection-safe).
- Pattern documented in CODE_STANDARDS; adopt for new/refactored queries;
  it type-enforces user-scoping (the biggest data-isolation win) and
  removes Row=any at the call site.

typecheck:server + check:server clean; 248/248 server tests.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 16:23:50 -05:00
null 0cad1626b1 test(money): property-based proof of cent-exact invariants (fast-check, Pillar F)
A financial app must prove its money math, not sample it. tests/
moneyProperties.test.js fuzzes utils/money.mts: toCents always integer;
dollars→cents→dollars == roundMoney (no drift); sumMoney is cent-exact;
integer cents round-trip; classic float-drift cases exact; invalid inputs
never yield a bogus number. 6 properties, all hold.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 16:08:50 -05:00
null e7196abbdf fix(security): patch react-router open-redirect (non-breaking audit fix)
`npm audit fix`: react-router/react-router-dom 6.30.3 → 6.30.4 (moderate
open-redirect via protocol-relative URL), nodemailer 8.0.9 → 8.0.11.
Verified: server 232/232, client 50/50, build green.

Remaining audit findings are assessed non-actionable/mitigated (see
SECURITY.md): nodemailer `raw` send option is not used; xlsx is confined
to the import service with raw:false + size/content-type limits.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 14:36:18 -05:00
null 5aaf292b2e ci(enforce): run the full gate in CI + add lefthook pre-commit + Knip
Pillar A enforcement so standards can't drift:
- .forgejo CI now runs format:check + lint (client+server) + typecheck
  (client) + typecheck:server, in addition to the existing check:server /
  tests / build. Previously lint and typecheck were NOT run in CI.
- lefthook pre-commit auto-formats staged files (prettier) + lints;
  pre-push typechecks. Auto-installed via the `prepare` script.
- Knip config (advisory `npm run knip`) for dead-code/unused-export
  review; the design-system ui/** is ignored to keep signal clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 14:27:03 -05:00
null f65138a114 chore(tooling): add Prettier + editorconfig, extend ESLint to the server
Standardization foundation (Pillar A):
- Prettier (.prettierrc.json, .prettierignore) + `format`/`format:check`
  scripts; style tuned to the house style (single quotes, semicolons,
  2-space, printWidth 100).
- .editorconfig for editor-level consistency.
- Extend the flat ESLint config to lint server `.cts` (Node globals, TS
  parser, "focused signal" rule set mirroring the client) so `eslint .`
  now covers both halves; `lint` → `eslint .`.
- Wire `format:check` into the `ci` script.

`npm run lint` is green (0 errors; pre-existing intentional patterns kept
as advisory warnings to ratchet later).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 14:23:53 -05:00
null f909e8f587 fix(docker): regenerate lockfile with npm 10 so `npm ci` works in build
The lockfile had drifted (written by local npm 11 / Node 25) into a form that
node:22-alpine's npm 10.9.8 reads as incomplete — `npm ci` failed with
"Missing: esbuild@0.28.1 from lock file", breaking the Docker image build
(Dockerfile now uses `npm ci`, not `npm install`). Regenerated the lock inside
node:22-alpine with its own npm so `npm ci` resolves cleanly (887 packages,
lockfileVersion 3). No dependency version changes — lockfile metadata only.

Verified: clean `npm ci` in node:22-alpine, full image build, container boots
migrations + workers, full QA (auth/CSRF/admin/security) and a live SimpleFIN
sync all pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 19:44:23 -05:00
null 3973560e92 refactor(tracker): remove dead code and unreferenced files (plan A1)
- Delete unreachable in-file dialogs: MonthlyStateDialog block (setShowMbs
  never called) and inline PaymentModal in TrackerRow + MobileTrackerRow
  (setEditPayment only ever null; editing works via PaymentLedgerDialog).
- Delete fully-unreferenced files: tracker/EditableCell.tsx,
  ui/confirm-dialog.tsx, ui/separator.tsx (+ orphaned @radix-ui/react-separator).
- Simplify OverdueCommandCenter dead branch (early-returns null when empty).
- Correct stale roadmap/FUTURE docs: tracker keyboard nav is implemented.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 14:35:13 -05:00
null 200c6373f5 feat(server-ts): migrate the money boundary to TypeScript on Node's native runtime (Phase 2)
Establishes the server-TypeScript foundation and migrates utils/money →
utils/money.mts with branded Cents/Dollars (mirroring the client), so the
cents↔dollars boundary — the origin of the reconciliation bug class — is a
compile error for any typed server caller.

Approach (no build step): Node 25 runs .ts natively (type-stripping).
Migrated modules use the explicit-ESM .mts extension (the project is
"type":"commonjs", which disables .ts ESM auto-detection) and are required
from the CJS callers via Node's require(esm) — verified working. Infra:
tsconfig.server.json (allowJs + checkJs:false → incremental like the
client), npm run typecheck:server, check:server extended to .mts, wired
into ci. 28 require sites repointed to money.mts.

typecheck:server clean; full server suite 225 + e2e probe 17/17 green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 13:44:04 -05:00
null 1d3b7435a0 feat(server): upgrade Express 4.18 → 5.2 (Phase 2)
The maintained line, and it forwards rejected async handlers to the error
middleware natively — closing the Express-4 gap where a throw in an async
route became an unhandled rejection that hung the request (this is why no
throwaway asyncHandler wrapper was needed). Small surface: only three
bare-* routes needed the path-to-regexp-v8 named-wildcard form
(/api/*splat, /legacy/*splat, /{*splat}); no removed APIs in use, all
req.query uses are reads. Suite 225 + probe 17/17 green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 13:25:00 -05:00
null 62a99fcaa4 build(lint): extend ESLint to .ts/.tsx via typescript-eslint
As files convert to TypeScript they must keep the react-hooks enforcement.
Added a .ts/.tsx config block using the typescript-eslint parser with the same
rules-of-hooks/exhaustive-deps/react-refresh rules; swapped core no-undef +
no-unused-vars (type-blind) for TS-aware equivalents. Verified 'npm run lint'
traverses .ts and flags issues there; 0 errors on the existing .ts files.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 21:44:35 -05:00
null 907a407399 build(ts): TypeScript foundation — tsconfig + typecheck in CI (T1)
Adopting TypeScript incrementally (the move off plain JS). Upgraded jsconfig ->
tsconfig with strict + noUncheckedIndexedAccess, but allowJs + checkJs:false so
every existing .js/.jsx keeps resolving and running untouched — only .ts/.tsx
get type-checked. Added 'npm run typecheck' (tsc --noEmit) and wired it into
'npm run ci'. Installed typescript 6 + @types/react/react-dom 19. Client-scoped
(Vite resolves the '@/' paths); the CommonJS server is a separate TS story.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 21:33:26 -05:00
null 47c031f1e4 build(react): enable React Compiler (React 19 auto-memoization) (F2)
Added babel-plugin-react-compiler to the Vite React plugin. The compiler
auto-memoizes components/hooks at build time, so manual useMemo/useCallback
become largely unnecessary going forward (existing ones are left in place —
harmless, and the compiler adds the rest). Safe here because the codebase is
rules-of-hooks clean (enforced by eslint-plugin-react-hooks).

Validated: production build succeeds and the e2e probe passes 17/17 with the
compiler on — every authed page renders axe-clean and Tracker/Summary/Analytics
reconciliation holds. (Build time ~2.2s -> ~6.2s from the compiler pass; the
runtime win is automatic memoization.) The compiler ESLint rule needs
eslint-plugin-react-hooks v6 — a future upgrade.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 21:20:26 -05:00
null c679022592 build(lint): add ESLint 9 + eslint-plugin-react-hooks/react-refresh (R1)
There was no linting at all — nothing enforced rules-of-hooks (conditional-hook
crashes) or exhaustive-deps (stale closures) across 125 client components/pages.
Added an ESLint flat config (eslint.config.mjs) scoped to client/, an 'npm run
lint' script, and the devDeps. First run: 0 rules-of-hooks violations (good),
6 errors + 13 exhaustive-deps warnings to work through next.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 19:41:24 -05:00
null 127b69ffc2 chore(qa): vendor chunk splitting, remove unused markdown deps, remove dead totalInterestPaid (batch 0.41.0 QA cleanup) 2026-07-02 20:47:50 -05:00
null 029c227685 fix(qa): seed demo data amounts, bill amount validation, negative USD format, a11y aria-labels, Playwright E2E setup (batch 0.41.0 QA) 2026-07-02 20:36:09 -05:00
null d9a441dff6 feat(settings): auto-save preferences with live save status (batch 0.39.0)
Replace all Save buttons on the Settings page with debounced auto-save:

- useAutoSave hook: debounce with latest-payload-wins, flush() for blur,
  pending-edit flush on unmount, status machine (idle/saving/saved/error)
  with saved fading back to idle. Covered by 6 Vitest tests (fake timers).
- SaveStatus pill (framer-motion) in the page header and notification card
  headers — Saving…/Saved/Save failed.
- Timing per control: toggles/selects/channel ~150-400ms; typed inputs
  (email, URLs, grace period, drift pct) 900ms + flush on blur.
- Push token never auto-saves mid-type: saves on blur only, so a partial
  token can never overwrite a working one.
- Notification cards no longer refetch parent settings on save (would
  clobber in-flight edits under auto-save).
- Decision: no undo toast — settings are non-destructive and instantly
  re-editable; undo would add noise without safety.
- vitest include now picks up .jsx tests; jsdom + @testing-library/react
  added as devDependencies.
2026-06-12 02:08:42 -05:00
null dc49eb9633 feat(cashflow): safe-to-spend projection with timeline, vitest setup, package upgrades 2026-06-12 01:32:28 -05:00
null ec7869abbc feat: framer-motion page transitions and UI polish 2026-06-07 15:14:09 -05:00
null 6d60eebe1a chore: dependency updates and UI fixes (batch) 2026-06-07 14:23:19 -05:00
null 4f5a3d0cff feat: bank sync section, data sources route, subscription page updates, package updates 2026-06-07 02:03:00 -05:00
null 83e6afa9e6 feat(subscriptions): simplified SubscriptionsPage, inline actions, improved matching card, Service Catalog route
- Extracted known-service catalog to dedicated /subscriptions/catalog route
- Simplified main Subscriptions page to focus on tracked services + bank-backed recommendations
- Replaced inline Pause/Resume with Edit + MoreHorizontal dropdown on subscription rows
- Added 'Improve Matching' card linking to Service Catalog
- Vite proxy respects API_PORT env var for dev flexibility
- Added top_200_us_subscriptions_researched dataset
- Updated HISTORY.md with v0.35.0 changes
2026-06-06 22:09:34 -05:00
null 653dd72e12 feat: TOTP 2FA for login & profile setup flow 2026-06-04 04:10:14 -05:00
null 71dfbe36cc refactor: component splits, PWA support, CommandPalette
Component Splits:
  - AdminPage.jsx: 1,906 -> 82 lines (logic moved to client/components/admin/ — 9 files)
  - DataPage.jsx: 3,132 -> 60 lines (logic moved to client/components/data/ — 8 files)
  - TrackerPage.jsx: 2,566 -> 2,132 lines (MonthlyStateDialog, StartingAmountsEditDialog, PaymentModal)

PWA:
  - vite-plugin-pwa installed with NetworkFirst caching for API routes
  - Square PWA icons (192x192, 512x512, apple-touch-icon)
  - theme-color, apple meta tags, touch icon in index.html
  - Build generates dist/sw.js + Workbox runtime

CommandPalette:
  - Navigation commands, Add bill action, month jumps
  - Grouped results with empty/filtered states
2026-05-28 20:53:22 -05:00
null 8cab248959 security fixes 2026-05-28 03:59:35 -05:00
null 9d933f70cc v0.28.01 2026-05-16 20:26:09 -05:00
null 0ba315bd32 v0.28.0 2026-05-15 22:45:38 -05:00
null 2ce5328fd2 v0.25.0: roadmap redesign, import CSRF fix, AdminDashboard removed
- RoadmapPage: kanban-style priority lanes, shadcn Collapsible/Tabs,
  lazy-loaded activity log, admin-only /api/about/roadmap + /dev-log endpoints
- Import CSRF fix: added x-csrf-token header to importAdminBackup,
  previewSpreadsheetImport, previewUserDbImport raw fetch() calls
- Removed AdminDashboard.jsx, replaced by RoadmapPage
- Added @radix-ui/react-collapsible + collapsible shadcn component
- Security audit by Private_Hudson: PASS (CSRF fix verified,
  admin endpoints gated, path traversal mitigated, XSS safe)
2026-05-11 21:42:36 -05:00
null d67fe6e61d v0.22.0: React Query Migration
- Added @tanstack/react-query and @tanstack/react-query-devtools
- Created useTracker, useBills, useCategories custom hooks (useQueries.js)
- Migrated TrackerPage from manual useState/useEffect to useQuery
- Added QueryClientProvider with 2min staleTime, 1 retry, refetchOnWindowFocus: false
- Added ReactQueryDevtools for development
- Fixed error handling: useRef pattern prevents duplicate toast notifications
- Replaced load() callback with refetch() from useQuery
- Hudson security audit: 4/5 PASS (1 FAIL fixed: error handling toast duplication)
2026-05-10 03:10:43 -05:00
null 6c7d481494 feat: add admin about page with security hardening
- Add /api/about-admin endpoint (admin-only, path traversal protection, content redaction, error sanitization)
- Add /admin/about route with RequireAuth admin guard
- Add adminActionLimiter rate limiting on about-admin endpoint
- Add rehype-sanitize XSS prevention in AboutPage.jsx
- Add aboutAdmin API client endpoint
- Create HISTORY.md with version bump convention (patch/minor/major)
- Update Engineering Reference Manual with about-admin docs and security measures
- Add INIT_REGULAR_USER/INIT_REGULAR_PASS env vars to docs
- Update FUTURE.md with critical regular user env var item
2026-05-09 16:25:12 -05:00
kaspa 4d1709aea3 push 2026-05-09 13:03:36 -05:00
_null 3228332e8c push 2026-05-04 23:34:24 -05:00
_null d1efeece04 push 2026-05-04 20:12:57 -05:00
_null 969139251d calendar 2026-05-04 13:14:32 -05:00
_null b9d1366d46 initial commit 2026-05-03 19:51:57 -05:00