Files
pn-new-crm/package.json

155 lines
5.3 KiB
JSON
Raw Normal View History

{
"name": "port-nimara-crm",
"version": "0.1.0",
"private": true,
fix(audit-tier-6): validation, perms, ops/infra, per-port webhook secret Final audit polish — closes the remaining LOW + MED items the previous tiers didn't reach: * Validation hardening: me.preferences uses .strict() + 8KB cap instead of unbounded .passthrough(); files.uploadFile gains magic-byte verification (jpeg/png/gif/webp/pdf/doc/xlsx); OCR scan endpoint enforces 10MB cap + magic-byte check on receipt images; port logoUrl + me.avatarUrl reject javascript:/data: schemes via a shared httpUrl refinement. * Permission gates: document-sends/{brochure,berth-pdf} now require email.send (was withAuth-only); document-sends/{preview,list} on email.view; ai/email-draft on email.send; documents/[id]/send uses send_for_signing (was create); expenses/export/parent-company flips from hard isSuperAdmin to expenses.export for parity; admin/users/options gated on reminders.assign_others (was withAuth). * Envelope hygiene: auth/set-password switches the third {message} variant to errorResponse + {data: {email}}; ai/email-draft wraps jobId in {data: {jobId}}. * UI polish: reports-list.handleDownload surfaces failures via toastError (was console-only). * Ops/infra: pin pnpm@10.33.2 across all three Dockerfiles + packageManager field in package.json; Dockerfile.worker re-orders user creation BEFORE pnpm install so node_modules / .cache dirs are worker-owned (fixes tesseract.js + sharp EACCES at first PDF parse); add Redis-ping HEALTHCHECK to the worker container. * Public health endpoint: returns full env+appUrl payload only when the caller presents X-Intake-Secret, otherwise a minimal {status} so generic uptime monitors still work but anonymous internet doesn't get deployment fingerprints. * Per-port Documenso webhook secret: new system_settings key + listDocumensoWebhookSecrets() helper. The webhook receiver iterates every configured per-port secret with timing-safe comparison + falls back to env, then forwards the resolved portId into handleDocumentExpired so two ports sharing a documensoId cannot cross-mutate. Deferred (handled in dedicated follow-up PRs): * Tier 5.1 — direct service tests for portal-auth / users / email-accounts / document-sends / sales-email-config. MED, large test-writing scope. * The {ok: true} → {data: null} envelope migration across alerts/expenses/admin-ocr-settings/storage routes. Mechanical but needs coordinated client + test updates. * CSP-nonce migration (drop unsafe-inline) — needs middleware-level nonce generation that the Next 15 router has to thread through. * Idempotency-Key header on Documenso createDocument. Requires schema column on documents to persist the key; deferred so it doesn't bundle a migration into this commit. * The 16 better-auth user_id FKs — separate dedicated migration with care (some columns are NOT NULL today and cascade decisions matter). * PermissionGate / Skeleton / EmptyState wraps across 5 admin lists (auditor-H §§36–37) and the residential-clients filter bar. Test status: 1175/1175 vitest, tsc clean. Refs: docs/audit-comprehensive-2026-05-05.md MED §§28,29,30 + LOW §§32–43 + HIGH §9 (Documenso secrets follow-up). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 21:03:31 +02:00
"packageManager": "pnpm@10.33.2",
"scripts": {
"dev": "next dev --turbopack -H 0.0.0.0",
"build": "next build && pnpm build:server",
"build:server": "esbuild src/server.ts --bundle --platform=node --target=node20 --format=cjs --outdir=dist --packages=external --tsconfig=tsconfig.server.json",
"build:worker": "esbuild src/worker.ts --bundle --platform=node --target=node20 --format=cjs --outdir=dist --packages=external --tsconfig=tsconfig.server.json",
"start": "next start",
"lint": "next lint",
"format": "prettier --write \"src/**/*.{ts,tsx,json,css}\"",
"db:generate": "drizzle-kit generate",
"db:push": "drizzle-kit push",
"db:studio": "drizzle-kit studio",
"db:seed": "tsx src/lib/db/seed.ts",
"db:seed:realistic": "tsx src/lib/db/seed.ts",
"db:seed:synthetic": "tsx src/lib/db/seed-synthetic.ts",
"db:reset": "tsx scripts/db-reset.ts --confirm",
"db:reseed:realistic": "pnpm db:reset && pnpm db:seed:realistic",
"db:reseed:synthetic": "pnpm db:reset && pnpm db:seed:synthetic",
"db:backfill:doc-folders": "tsx scripts/backfill-document-folders.ts",
test(e2e): exhaustive click-through suite + destructive narrow tests PR 14: adds a tier-3.5 Playwright pass that opens every refactored page, clicks every visible button/link/role=button, and asserts no console errors, no app-side network 4xx/5xx, and no click-time exceptions. Helper: - tests/helpers/click-everything.ts — shared `clickEverythingOnPage` with default skips for destructive selectors (archive, delete, transfer, sign-out), auto-closing of dialogs, and return-to-start after navigation. Exhaustive specs (tests/e2e/exhaustive/): - 01-yachts: list + detail + transfer dialog - 02-companies: list + detail + add-membership dialog - 03-reservations: berth list + detail reservations tab + reserve dialog - 04-client-detail: list + detail walking every tab - 05-eoi-generate: generate dialog opens with Documenso option - 06-invoice-form: new-invoice dialog billing-entity toggle - 07-berths: list + detail walking every tab - 08-portal: client portal yachts / memberships / reservations - 09-navigation: every primary nav target loads cleanly Destructive specs (tests/e2e/destructive/): - 01-yacht-archive: create-via-API → archive via UI → assert removed. Skips with a clear message when the global setup does not seed an owner client (avoids brittle failures while the full destructive fixture lands). Playwright config: testDir hoisted to ./tests/e2e; new `exhaustive` and `destructive` projects share the existing setup project. New scripts test:e2e / test:e2e:smoke / test:e2e:exhaustive / test:e2e:destructive in package.json drive each project independently. CI integration deferred — no .github/workflows/* exists in this repo yet, so the PR 14 task to wire a separate CI job is N/A. The new projects will pick up automatically when a workflow lands. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 14:06:10 +02:00
"test:e2e": "playwright test",
"test:e2e:smoke": "playwright test --project=smoke",
"test:e2e:exhaustive": "playwright test --project=exhaustive",
"test:e2e:destructive": "playwright test --project=destructive",
"prepare": "husky || true"
},
"dependencies": {
"@dnd-kit/core": "^6.3.1",
"@dnd-kit/sortable": "^10.0.0",
"@dnd-kit/utilities": "^3.2.2",
"@formkit/auto-animate": "^0.9.0",
feat(deps): bump zod 3→4 + @hookform/resolvers 3→5 Resolved 65 type errors across the codebase via these v4 migration patterns: - `ZodError.errors` renamed to `ZodError.issues` (4 call sites in auth routes + central error handler). - `z.record(value)` now requires explicit key type: `z.record(z.string(), value)`. Updated 7 sites across templates / forms / saved-views / website-inquiries. - `.refine(check, msgFn)` second-arg shape changed — now requires an `{ error: (issue) => ... }` object form. Updated `mergeFieldsSchema` in document-templates validator. - `.transform(...).default(...)` chains: v4 enforces default value type matches transform OUTPUT. Reordered to `.default(...).transform(...)` in list-query / company-memberships handlers. - `z.coerce.*()` INPUT type widened to `unknown` in v4. Service signatures using `z.input<typeof schema>` (kept for caller flexibility around defaults) now re-parse via `schema.parse(data)` to recover the post-coercion shape Drizzle needs. Done in berth-reservations service. Invoice service narrows `lineItems` locally with a typed cast since re-parsing would double-validate. - `.optional().transform(...)` no longer propagates the optional marker through v4's new ZodPipe. Moved `.optional()` to the END of chain in `optionalDesiredDimSchema` (interests) and documents list query (folderId, signatureOnly). - ZodIssue subtype shapes simplified: `received` removed from invalid_type, `type` renamed to `origin` on too_small. Test fixtures updated. - @hookform/resolvers v5 splits Resolver into 3-generic form (Input, Context, Output). useForm calls in 6 forms (client, yacht, berth, interest, expense, invoices-new-page) now pass explicit generics: `useForm<z.input<typeof schema>, unknown, z.infer<typeof schema>>`. Verified: tsc clean (0 errors), vitest 1293/1293 pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 18:29:03 +02:00
"@hookform/resolvers": "^5.2.2",
fix(types): unblock catch-all routes under stricter Next 15.5 typing + Phase 2B deps Two changes bundled (build was failing on the type fix; deps came along on the same branch). 1. RouteHandler / withAuth / withPermission are now generic over the route's params shape. Default stays `Record<string, string>` for the common `[id]`-style routes (no caller changes needed). Catch-all routes like `[...path]` declare their narrow shape via a type-arg: export const PATCH = withAuth<{ path: string[] }>( withPermission<{ path: string[] }>('files', 'manage_folders', async (req, ctx, params) => { /* params.path: string[] */ } ), ); Without this, Next.js 15.5+'s stricter route-type checking rejected the build because the inferred `params: Promise<{ path: string[] }>` for `[...path]` doesn't satisfy `Promise<Record<string, string>>`. Updated `src/app/api/v1/files/folders/[...path]/route.ts` (the only catch-all in the tree right now) to use the new generic. 2. Phase 2B deps (within-major-jump where the API didn't actually break): - @pdfme/common, @pdfme/generator, @pdfme/schemas: 5.5.10 → 6.1.2 (closes 3 mod XSS/SSRF/decompression-bomb advisories) - lucide-react: 0.460.0 → 1.14.0 - sonner: 1.7.4 → 2.0.7 - tailwind-merge: 2.6.1 → 3.5.0 Tests: 1185/1185 vitest. tsc clean. Local `next build` succeeds. Reverted (deferred to a focused PR): - @hookform/resolvers 5: Resolver<T> typing change requires per-form useForm migration - eslint 10: incompatible with @rushstack/eslint-patch (pulled in by eslint-config-next) - react-day-picker 10: ClassNames removed `table`; needs calendar.tsx migration - zod 4: 94 type errors cascading through drizzle insert types; needs comprehensive migration Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 17:07:07 +02:00
"@pdfme/common": "^6.1.2",
"@pdfme/generator": "^6.1.2",
"@pdfme/schemas": "^6.1.2",
"@radix-ui/react-accordion": "^1.2.12",
"@radix-ui/react-alert-dialog": "^1.1.15",
"@radix-ui/react-avatar": "^1.1.11",
"@radix-ui/react-checkbox": "^1.3.3",
"@radix-ui/react-dialog": "^1.1.15",
"@radix-ui/react-dropdown-menu": "^2.1.16",
"@radix-ui/react-icons": "^1.3.2",
"@radix-ui/react-label": "^2.1.8",
"@radix-ui/react-navigation-menu": "^1.2.14",
"@radix-ui/react-popover": "^1.1.15",
"@radix-ui/react-progress": "^1.1.8",
"@radix-ui/react-radio-group": "^1.3.8",
"@radix-ui/react-scroll-area": "^1.2.10",
"@radix-ui/react-select": "^2.2.6",
"@radix-ui/react-separator": "^1.1.8",
"@radix-ui/react-slider": "^1.3.6",
"@radix-ui/react-slot": "^1.2.4",
"@radix-ui/react-switch": "^1.2.6",
"@radix-ui/react-tabs": "^1.1.13",
"@radix-ui/react-tooltip": "^1.2.8",
"@react-email/components": "^1.0.12",
"@socket.io/redis-adapter": "^8.3.0",
"@tanstack/query-broadcast-client-experimental": "^5.100.10",
deps: bump Tier-A patches + react-day-picker 10 + esbuild 0.28 Successfully bumped: - bullmq 5.76.6 → 5.76.8 - @tanstack/react-query 5.100.9 → 5.100.10 - @tanstack/react-query-devtools 5.100.9 → 5.100.10 - better-auth 1.6.9 → 1.6.10 - @playwright/test 1.59.1 → 1.60.0 - libphonenumber-js 1.12.43 → 1.13.1 - tailwind-merge 3.5.0 → 3.6.0 - vitest 4.1.5 → 4.1.6 - @vitest/coverage-v8 4.1.5 → 4.1.6 - lint-staged 17.0.3 → 17.0.4 - esbuild 0.27.7 → 0.28.0 - react-grab 0.1.33 → 0.1.34 - react-day-picker 9.14.0 → 10.0.0 react-day-picker 10 verified safe: probed v10 release notes against src/components/ui/calendar.tsx — we use only v9-canonical APIs that v10 preserves. Removed the `table` className entry from the wrapper (v10 dropped it since the renderer is now CSS-grid, not table-based). Tried + rolled back: - @hookform/resolvers 3 → 5: stricter input/output inference broke every form using <{schema}, any, {schema}> implicit shape. Needs per-form refactor; parked. Verified clean: pnpm audit (prod + dev) = 0 vulnerabilities; pnpm exec tsc --noEmit clean; vitest 1293/1293 pass. Remaining outdated (deliberately deferred — see docs/AUDIT-2026-05-12.md §34): - next/eslint-config-next 15 → 16 (2-4 wk wait) - zod 3 → 4 (couple with @hookform/resolvers 5; codemod-needed) - tailwindcss 3 → 4 (focused-afternoon project) - @types/node ^20.19 stays pinned to match runtime (audit decision) - archiver 7 stays (no @types/archiver@8 published) - eslint 9 stays (locked to eslint-config-next 15) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 17:33:24 +02:00
"@tanstack/react-query": "^5.100.10",
"@tanstack/react-query-devtools": "^5.100.10",
"@tanstack/react-table": "^8.21.3",
"@tanstack/react-virtual": "^3.13.24",
feat(expenses): streaming expense-PDF export + receipt-less expense flag + audit-3 fixes Replaces the legacy text-only expense PDF (was just dumping rows into a single pdfme text field — no images, no pagination) with a proper streaming export modelled on the legacy Nuxt client-portal but re-architected for memory safety. The legacy implementation OOM'd on hundreds of receipts because it: - buffered every receipt image into memory simultaneously - accumulated PDF chunks into an array, concat'd at end - base64-encoded the whole PDF into a JSON response (3x peak memory) - had no image downscaling The new design: - `streamExpensePdf()` (src/lib/services/expense-pdf.service.ts): pdfkit pipes bytes directly to the HTTP response (no Buffer accumulation). Receipts are processed serially so peak heap is one image at a time. Sharp downscales any receipt > 500 KB or > 1500 px to JPEG q80 — typical 8 MB phone photo collapses to ~250 KB. For a 500-receipt export, peak RSS stays under ~100 MB; legacy needed >2 GB for the same input. - Pages: cover summary box (count, totals, currency equiv, optional processing fee), grouped expense table (groupBy=none|payer|category| date), one-page-per-receipt with header (establishment, amount, date, payer, category, file name) and full-bleed image. - Storage backend abstraction — receipts stream from `getStorageBackend().get(storageKey)`, works on MinIO/S3/filesystem. - Route: POST /api/v1/expenses/export/pdf streams binary application/pdf with cache-control:no-store. Validator caps expenseIds at 1000 to prevent runaway loops. Receipt-less expense flow (per user request): - Schema: 0033 migration adds `expenses.no_receipt_acknowledged` boolean (default false). - Validator: createExpenseSchema requires either receiptFileIds OR noReceiptAcknowledged=true; the .refine() error message tells the rep exactly what to do. updateExpenseSchema is partial and skips the rule (existing rows can be edited without re-acknowledging). - PDF: receiptless expenses get an inline red "(no receipt)" tag in the establishment cell + a red footer warning in the summary box showing the count and at-risk amount. - The legacy parent-company reimbursement queue may refuse to pay receiptless expenses, so the warning is load-bearing for ops. Audit-3 fixes piggy-backed: - 🔴 Tesseract OCR runtime now races a 30s timeout (CPU-bomb DoS protection — a crafted PDF rasterizing to high-res noise could pin the worker indefinitely). - 🟠 brochures.service.ts:listBrochures dropped a wasted query (the legacy single-brochure fast-path was discarding its result on the multi-brochure branch). - 🟠 berth-pdf.service.ts:listBerthPdfVersions now Promise.all's the presignDownload calls instead of awaiting each in a for-loop — 20-version berths went from 20× round-trip to 1×. - 🟡 public berths route no longer logs the full `row` object on enum drift (was dumping price + amenity columns into ops logs). - 🟡 dropped the dead `void sql` import from public berths route. Tests still 1163/1163. tsc clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 04:38:32 +02:00
"@types/pdfkit": "^0.17.6",
feat(ui): broad consistency sweep — sources, dates, comboboxes, milestones Mobile + responsive - berth-form full-width on phones (was 480px fixed → overflowed iPhone) - currency-input switched to inputMode=decimal with live thousands separator - client-form Country/Timezone/Source/Preferred-Contact full-width <sm - contacts row restructured so Primary toggle + Remove get their own strip - customize-dashboard footer stacks vertically on mobile; Done full-width - interest-form client/berth pickers no longer cmdk-filter on UUID (typing "Carlos" now returns Carlos Vega instead of "No clients found") Data + consistency - SOURCES + SOURCE_LABELS + formatSource() in lib/constants; 9 surfaces now resolve interest/client source from one place - INTEREST_OUTCOMES adds lost_other (picker, badge, timeline) - Berth options natural-sort A1 → A2 → … → A10 via lib/utils/mooring-sort - archiver downgraded ^8 → ^7.0.1 so the GDPR export route compiles - TableBody last-row uses border-b-0 (not border-0); colored left-accent on the bottom berth row now renders - Hide Invite-to-Portal until port setting === true (was !== false default-show) - OwnerPicker primer query resolves entity name on first paint (no more UUID flash before the popover opens) Terminology - Replaced user-facing "Documenso" with "signing service" / "Generated EOI" / "Manual EOI" in 8 components (admin/internal references kept) - Plainer status-change copy on berth-detail-header Forms + editing - InlineEditableField gained a `date` variant (native picker); applied to company incorporation date and ready for other YYYY-MM-DD plaintext fields - Inline source picker on interest-tabs detail (was free text) - TagPicker self-hides when port has no tags AND nothing is selected - New ReminderDaysInput with preset chips (1d / 3d / 1wk / 2wk / 1mo / custom) - Compose dialog follow-up is now a toggle that reveals datetime picker Pipeline milestones - changeStageSchema accepts optional milestoneDate; service stamps it on the matching date column instead of always using now - MilestoneAdvanceButton popover collects a back-date before stage advance - Applied to every "Mark X manually" surface on the interest overview EOI / linked-berths polish - Add-bypass row aligned inline with toggle descriptions - Tooltips on "Specifically pitching" / "Mark in EOI bundle" explain their legal vs. public-map consequences Surfaces - Companies list now has the column picker + persisted hidden-column prefs - NotesList aggregate flag enabled on clients, companies, residential_clients (yachts already aggregated) ft/m unit toggle (interim, before drift fix) - "Berth size desired" gets a section-level ft/m toggle; per-field hint shows the converted value. Storage stays canonical-ft for now; the drift-safe persistence migration is the next step. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 14:50:58 +02:00
"archiver": "^7.0.1",
deps: bump Tier-A patches + react-day-picker 10 + esbuild 0.28 Successfully bumped: - bullmq 5.76.6 → 5.76.8 - @tanstack/react-query 5.100.9 → 5.100.10 - @tanstack/react-query-devtools 5.100.9 → 5.100.10 - better-auth 1.6.9 → 1.6.10 - @playwright/test 1.59.1 → 1.60.0 - libphonenumber-js 1.12.43 → 1.13.1 - tailwind-merge 3.5.0 → 3.6.0 - vitest 4.1.5 → 4.1.6 - @vitest/coverage-v8 4.1.5 → 4.1.6 - lint-staged 17.0.3 → 17.0.4 - esbuild 0.27.7 → 0.28.0 - react-grab 0.1.33 → 0.1.34 - react-day-picker 9.14.0 → 10.0.0 react-day-picker 10 verified safe: probed v10 release notes against src/components/ui/calendar.tsx — we use only v9-canonical APIs that v10 preserves. Removed the `table` className entry from the wrapper (v10 dropped it since the renderer is now CSS-grid, not table-based). Tried + rolled back: - @hookform/resolvers 3 → 5: stricter input/output inference broke every form using <{schema}, any, {schema}> implicit shape. Needs per-form refactor; parked. Verified clean: pnpm audit (prod + dev) = 0 vulnerabilities; pnpm exec tsc --noEmit clean; vitest 1293/1293 pass. Remaining outdated (deliberately deferred — see docs/AUDIT-2026-05-12.md §34): - next/eslint-config-next 15 → 16 (2-4 wk wait) - zod 3 → 4 (couple with @hookform/resolvers 5; codemod-needed) - tailwindcss 3 → 4 (focused-afternoon project) - @types/node ^20.19 stays pinned to match runtime (audit decision) - archiver 7 stays (no @types/archiver@8 published) - eslint 9 stays (locked to eslint-config-next 15) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 17:33:24 +02:00
"better-auth": "^1.6.10",
"bullmq": "^5.76.8",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"cmdk": "^1.1.1",
"date-fns": "^4.1.0",
"drizzle-orm": "^0.45.2",
"imapflow": "^1.3.3",
"ioredis": "^5.10.1",
feat(i18n): country/phone/timezone/subdivision primitives + form wiring Cross-cutting i18n polish for forms across the marina + residential + company domains. Introduces a single source of truth for country/phone/timezone/ subdivision data and replaces every nationality-as-free-text and timezone- as-string Input with a dedicated combobox. PR1 Countries — ALL_COUNTRY_CODES (~250 ISO-3166-1 alpha-2), Intl.DisplayNames for localized labels, detectDefaultCountry() with navigator-region fallback to US, CountryCombobox with regional-indicator flag glyphs + compact mode for inline use. PR2 Phone — libphonenumber-js wrapper (parsePhone / formatAsYouType / callingCodeFor), PhoneInput with flag dropdown + national-format AsYouType + paste-detect that flips the country dropdown for pasted international strings. PR3 Timezones — country->IANA map (250 entries, multi-zone for AU/BR/CA/CD/ ID/KZ/MN/MX/RU/US), formatTimezoneLabel ("Europe/London (UTC+1)"), TimezoneCombobox with Suggested/All grouping driven by countryHint. PR4 Subdivisions — wraps the iso-3166-2 npm package (~5000 ISO 3166-2 codes for every country), per-country cache, SubdivisionCombobox with "Pick a country first" / "No regions available" empty states. PR5 Schema deltas (migration 0015) — clients.nationality_iso, clientContacts {value_e164, value_country}, clientAddresses {country_iso, subdivision_iso}, residentialClients {phone_e164, phone_country, nationality_iso, timezone, place_of_residence_country_iso, subdivision_iso}, companies {incorporation_ country_iso, incorporation_subdivision_iso}, companyAddresses {country_iso, subdivision_iso}. Plus shared zod validators (validators/i18n.ts) used by every entity validator + route handler. PR6 ClientForm + ClientDetail — CountryCombobox replaces nationality Input, TimezoneCombobox replaces timezone Input (driven by nationalityIso hint), PhoneInput conditionally rendered for phone/whatsapp contacts. Inline editors (InlineCountryField / InlineTimezoneField / InlinePhoneField) for the detail-page overview rows + ContactsEditor. PR7 Residential client form + detail — phone -> PhoneInput, nationality/ timezone/place-of-residence-country/subdivision rows in both create sheet and inline-editable detail view. Subdivision wipes when country flips since codes are country-scoped. PR8 Company form + detail — incorporation country -> CountryCombobox, incorporation region -> SubdivisionCombobox in both modes. PR9 Public inquiry endpoint — accepts pre-normalized phoneE164/phoneCountry and i18n fields from newer website builds, server-side parsePhone() fallback for legacy raw-international submissions. Old Nuxt builds keep working unchanged. Tests: 4 unit suites for the primitives (25 tests), 1 integration spec for the public phone-normalization path (3 tests), 1 smoke spec asserting the combobox triggers render in all three create sheets. Test totals: vitest 713 -> 741 (+28). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 18:13:08 +02:00
"iso-3166-2": "^1.0.0",
"isomorphic-dompurify": "^3.12.0",
"jose": "^6.2.3",
deps: bump Tier-A patches + react-day-picker 10 + esbuild 0.28 Successfully bumped: - bullmq 5.76.6 → 5.76.8 - @tanstack/react-query 5.100.9 → 5.100.10 - @tanstack/react-query-devtools 5.100.9 → 5.100.10 - better-auth 1.6.9 → 1.6.10 - @playwright/test 1.59.1 → 1.60.0 - libphonenumber-js 1.12.43 → 1.13.1 - tailwind-merge 3.5.0 → 3.6.0 - vitest 4.1.5 → 4.1.6 - @vitest/coverage-v8 4.1.5 → 4.1.6 - lint-staged 17.0.3 → 17.0.4 - esbuild 0.27.7 → 0.28.0 - react-grab 0.1.33 → 0.1.34 - react-day-picker 9.14.0 → 10.0.0 react-day-picker 10 verified safe: probed v10 release notes against src/components/ui/calendar.tsx — we use only v9-canonical APIs that v10 preserves. Removed the `table` className entry from the wrapper (v10 dropped it since the renderer is now CSS-grid, not table-based). Tried + rolled back: - @hookform/resolvers 3 → 5: stricter input/output inference broke every form using <{schema}, any, {schema}> implicit shape. Needs per-form refactor; parked. Verified clean: pnpm audit (prod + dev) = 0 vulnerabilities; pnpm exec tsc --noEmit clean; vitest 1293/1293 pass. Remaining outdated (deliberately deferred — see docs/AUDIT-2026-05-12.md §34): - next/eslint-config-next 15 → 16 (2-4 wk wait) - zod 3 → 4 (couple with @hookform/resolvers 5; codemod-needed) - tailwindcss 3 → 4 (focused-afternoon project) - @types/node ^20.19 stays pinned to match runtime (audit decision) - archiver 7 stays (no @types/archiver@8 published) - eslint 9 stays (locked to eslint-config-next 15) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 17:33:24 +02:00
"libphonenumber-js": "^1.13.1",
fix(types): unblock catch-all routes under stricter Next 15.5 typing + Phase 2B deps Two changes bundled (build was failing on the type fix; deps came along on the same branch). 1. RouteHandler / withAuth / withPermission are now generic over the route's params shape. Default stays `Record<string, string>` for the common `[id]`-style routes (no caller changes needed). Catch-all routes like `[...path]` declare their narrow shape via a type-arg: export const PATCH = withAuth<{ path: string[] }>( withPermission<{ path: string[] }>('files', 'manage_folders', async (req, ctx, params) => { /* params.path: string[] */ } ), ); Without this, Next.js 15.5+'s stricter route-type checking rejected the build because the inferred `params: Promise<{ path: string[] }>` for `[...path]` doesn't satisfy `Promise<Record<string, string>>`. Updated `src/app/api/v1/files/folders/[...path]/route.ts` (the only catch-all in the tree right now) to use the new generic. 2. Phase 2B deps (within-major-jump where the API didn't actually break): - @pdfme/common, @pdfme/generator, @pdfme/schemas: 5.5.10 → 6.1.2 (closes 3 mod XSS/SSRF/decompression-bomb advisories) - lucide-react: 0.460.0 → 1.14.0 - sonner: 1.7.4 → 2.0.7 - tailwind-merge: 2.6.1 → 3.5.0 Tests: 1185/1185 vitest. tsc clean. Local `next build` succeeds. Reverted (deferred to a focused PR): - @hookform/resolvers 5: Resolver<T> typing change requires per-form useForm migration - eslint 10: incompatible with @rushstack/eslint-patch (pulled in by eslint-config-next) - react-day-picker 10: ClassNames removed `table`; needs calendar.tsx migration - zod 4: 94 type errors cascading through drizzle insert types; needs comprehensive migration Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 17:07:07 +02:00
"lucide-react": "^1.14.0",
"mailparser": "^3.9.8",
"minio": "^8.0.7",
"next": "15.5.18",
"next-themes": "^0.4.6",
"nodemailer": "^8.0.7",
"openai": "^6.37.0",
"p-limit": "^7.3.0",
"pdf-lib": "^1.17.1",
feat(expenses): streaming expense-PDF export + receipt-less expense flag + audit-3 fixes Replaces the legacy text-only expense PDF (was just dumping rows into a single pdfme text field — no images, no pagination) with a proper streaming export modelled on the legacy Nuxt client-portal but re-architected for memory safety. The legacy implementation OOM'd on hundreds of receipts because it: - buffered every receipt image into memory simultaneously - accumulated PDF chunks into an array, concat'd at end - base64-encoded the whole PDF into a JSON response (3x peak memory) - had no image downscaling The new design: - `streamExpensePdf()` (src/lib/services/expense-pdf.service.ts): pdfkit pipes bytes directly to the HTTP response (no Buffer accumulation). Receipts are processed serially so peak heap is one image at a time. Sharp downscales any receipt > 500 KB or > 1500 px to JPEG q80 — typical 8 MB phone photo collapses to ~250 KB. For a 500-receipt export, peak RSS stays under ~100 MB; legacy needed >2 GB for the same input. - Pages: cover summary box (count, totals, currency equiv, optional processing fee), grouped expense table (groupBy=none|payer|category| date), one-page-per-receipt with header (establishment, amount, date, payer, category, file name) and full-bleed image. - Storage backend abstraction — receipts stream from `getStorageBackend().get(storageKey)`, works on MinIO/S3/filesystem. - Route: POST /api/v1/expenses/export/pdf streams binary application/pdf with cache-control:no-store. Validator caps expenseIds at 1000 to prevent runaway loops. Receipt-less expense flow (per user request): - Schema: 0033 migration adds `expenses.no_receipt_acknowledged` boolean (default false). - Validator: createExpenseSchema requires either receiptFileIds OR noReceiptAcknowledged=true; the .refine() error message tells the rep exactly what to do. updateExpenseSchema is partial and skips the rule (existing rows can be edited without re-acknowledging). - PDF: receiptless expenses get an inline red "(no receipt)" tag in the establishment cell + a red footer warning in the summary box showing the count and at-risk amount. - The legacy parent-company reimbursement queue may refuse to pay receiptless expenses, so the warning is load-bearing for ops. Audit-3 fixes piggy-backed: - 🔴 Tesseract OCR runtime now races a 30s timeout (CPU-bomb DoS protection — a crafted PDF rasterizing to high-res noise could pin the worker indefinitely). - 🟠 brochures.service.ts:listBrochures dropped a wasted query (the legacy single-brochure fast-path was discarding its result on the multi-brochure branch). - 🟠 berth-pdf.service.ts:listBerthPdfVersions now Promise.all's the presignDownload calls instead of awaiting each in a for-loop — 20-version berths went from 20× round-trip to 1×. - 🟡 public berths route no longer logs the full `row` object on enum drift (was dumping price + amenity columns into ops logs). - 🟡 dropped the dead `void sql` import from public berths route. Tests still 1163/1163. tsc clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 04:38:32 +02:00
"pdfkit": "^0.18.0",
"pino": "^10.3.1",
"pino-pretty": "^13.1.3",
"postgres": "^3.4.9",
"react": "^19.2.6",
deps: bump Tier-A patches + react-day-picker 10 + esbuild 0.28 Successfully bumped: - bullmq 5.76.6 → 5.76.8 - @tanstack/react-query 5.100.9 → 5.100.10 - @tanstack/react-query-devtools 5.100.9 → 5.100.10 - better-auth 1.6.9 → 1.6.10 - @playwright/test 1.59.1 → 1.60.0 - libphonenumber-js 1.12.43 → 1.13.1 - tailwind-merge 3.5.0 → 3.6.0 - vitest 4.1.5 → 4.1.6 - @vitest/coverage-v8 4.1.5 → 4.1.6 - lint-staged 17.0.3 → 17.0.4 - esbuild 0.27.7 → 0.28.0 - react-grab 0.1.33 → 0.1.34 - react-day-picker 9.14.0 → 10.0.0 react-day-picker 10 verified safe: probed v10 release notes against src/components/ui/calendar.tsx — we use only v9-canonical APIs that v10 preserves. Removed the `table` className entry from the wrapper (v10 dropped it since the renderer is now CSS-grid, not table-based). Tried + rolled back: - @hookform/resolvers 3 → 5: stricter input/output inference broke every form using <{schema}, any, {schema}> implicit shape. Needs per-form refactor; parked. Verified clean: pnpm audit (prod + dev) = 0 vulnerabilities; pnpm exec tsc --noEmit clean; vitest 1293/1293 pass. Remaining outdated (deliberately deferred — see docs/AUDIT-2026-05-12.md §34): - next/eslint-config-next 15 → 16 (2-4 wk wait) - zod 3 → 4 (couple with @hookform/resolvers 5; codemod-needed) - tailwindcss 3 → 4 (focused-afternoon project) - @types/node ^20.19 stays pinned to match runtime (audit decision) - archiver 7 stays (no @types/archiver@8 published) - eslint 9 stays (locked to eslint-config-next 15) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 17:33:24 +02:00
"react-day-picker": "^10.0.0",
"react-dom": "^19.2.6",
feat: autonomous backlog push — admin UX overhaul + storage parity + residential parity + Documenso Phase 1 Massive multi-area push driven by docs/admin-ux-backlog.md. Every byte path now goes through getStorageBackend() so signed EOIs, contracts, brochures, berth PDFs, files, avatars, branding logos, and DB backups all work identically on S3 and filesystem backends. USER SETTINGS (rebuild) - Country + Timezone selectors with cross-defaulting - Browser-detected timezone banner ("Looks like you're in Europe/Paris…") - Email change with verification flow (user_email_changes table, OLD-address cancel link + NEW-address confirm link) + EMAIL_CHANGE_INSTANT=true dev shortcut - Password reset triggered via better-auth requestPasswordReset - Profile photo upload + crop (square 256×256) via shared <ImageCropperDialog> + /api/v1/me/avatar BRANDING - Shared <ImageCropperDialog> using react-easy-crop - Logo upload + crop in /admin/branding (writes via /api/v1/admin/settings/image -> storage backend) - Email header/footer HTML defaults injectable via "Insert default" - SettingsFormCard new field types: timezone (combobox), image-upload STORAGE ADMIN OVERHAUL - S3 config form FIRST, swap action SECOND - Test connection before any switch - Two-button switch: "Switch + migrate" vs "Switch only" with warning modals - runMigration() honours skipMigration flag - /api/ready + system-monitoring health check use the active storage backend instead of always probing MinIO - Filesystem backend already had full feature parity — verified BACKUP MANAGEMENT (real) - New backup_jobs table (id / status / trigger / size / storage_path) - runBackup() service spawns pg_dump --format=custom, streams to active storage backend via getStorageBackend().put() - /admin/backup page: trigger, history, download .dump for restore - Super-admin gated AI ADMIN PANEL - /admin/ai consolidates master switch + monthly token cap + provider credentials - Per-feature settings (OCR, berth-PDF parser, recommender) linked from the same page ONBOARDING WIZARD - /admin/onboarding now real with auto-checked steps - Reads each setting key + lists endpoint (roles/users/tags) to decide completion - Manual checkboxes for steps without an auto-detect signal - Progress bar + Mark done/Mark incomplete buttons - State persisted in system_settings.onboarding_manual_status RESIDENTIAL PARITY (full) - New residential_client_notes + residential_interest_notes tables (mirror marina-side shape) - Polymorphic notes.service.ts extended (verifyParent, listForEntity, create, update, delete) for residential_clients/_interests - <NotesList> component accepts the new entity types - 4 new note endpoints (GET/POST/PATCH/DELETE for clients + interests) - 2 new activity endpoints (residential clients + interests) - residential-client-tabs.tsx + residential-interest-tabs.tsx use DetailLayout (Overview / Interests / Notes / Activity) - residential-client-detail-header.tsx mirrors marina-side strip - useBreadcrumbHint wired into both detail components - Configurable Assigned-to dropdown (residential_interests.view perm) CONFIGURABLE RESIDENTIAL STAGES - residential-stages.service.ts with list / save / orphan-check - /api/v1/residential/stages GET/PUT - /admin/residential-stages admin UI with reassign-on-remove modal - Validators relaxed from z.enum to z.string DOCUMENSO PHASE 1 - Schema: document_signers.invited_at / opened_at / last_reminder_sent_at / signing_token (+ idx_ds_signing_token) - Schema: documents.completion_cc_emails (text[]) + auto_reminder_interval_days (int) - transformSigningUrl() now maps SignerRole -> URL segment via ROLE_TO_URL_SEGMENT (approver->cc, witness->witness) — fixes Risk #5 where approver invites landed on /sign/error - POST /api/v1/documents/[id]/send-invitation with auto-pick of next pending signer - Per-port settings: documenso_developer_label / _approver_label + documenso_developer_user_id / _approver_user_id (Phase 7 Project Director RBAC binding fields) ADMIN UX RAPID-FIRE - Sidebar collapse removed (always-expanded design) - Audit log: input sizes (h-9), date pickers w-44, action cell sub-label so single-row entries aren't blank - Sales email config: token list <details> + tooltips on threshold + body fields - Custom Settings card: long-form description - Reminder digest timezone uses TimezoneCombobox - Port form: currency dropdown (10 common currencies) + timezone combobox + brand color picker - Permissions count badge opens modal with granted/denied per resource - Role names display-normalized via prettifyRoleName - Tag form: native input type=color - Custom Fields page: amber heads-up about non-integration - Settings manager: select field type + fallthrough_policy as dropdown - Storage admin S3 fields ship as proper password + boolean LIST PAGES - Residential client list: clickable email/phone (mailto/tel/wa.me) - Residential interests + Documents Hub search inputs sized h-9 CURRENCY API - scripts/test-currency-api.ts verifies live Frankfurter fetch -> DB upsert -> getRate -> convert. Inverse-rate drift <=0.001 TESTS - 1185/1185 vitest passing - tsc clean - eslint 0 errors (16 pre-existing warnings) Note: WEBSITE_INTAKE_SECRET added to .env.example but committed separately due to pre-commit hook policy on .env* files. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 21:02:12 +02:00
"react-easy-crop": "^5.5.7",
"react-email": "^6.1.3",
"react-hook-form": "^7.75.0",
"recharts": "^3.8.1",
feat(expenses): streaming expense-PDF export + receipt-less expense flag + audit-3 fixes Replaces the legacy text-only expense PDF (was just dumping rows into a single pdfme text field — no images, no pagination) with a proper streaming export modelled on the legacy Nuxt client-portal but re-architected for memory safety. The legacy implementation OOM'd on hundreds of receipts because it: - buffered every receipt image into memory simultaneously - accumulated PDF chunks into an array, concat'd at end - base64-encoded the whole PDF into a JSON response (3x peak memory) - had no image downscaling The new design: - `streamExpensePdf()` (src/lib/services/expense-pdf.service.ts): pdfkit pipes bytes directly to the HTTP response (no Buffer accumulation). Receipts are processed serially so peak heap is one image at a time. Sharp downscales any receipt > 500 KB or > 1500 px to JPEG q80 — typical 8 MB phone photo collapses to ~250 KB. For a 500-receipt export, peak RSS stays under ~100 MB; legacy needed >2 GB for the same input. - Pages: cover summary box (count, totals, currency equiv, optional processing fee), grouped expense table (groupBy=none|payer|category| date), one-page-per-receipt with header (establishment, amount, date, payer, category, file name) and full-bleed image. - Storage backend abstraction — receipts stream from `getStorageBackend().get(storageKey)`, works on MinIO/S3/filesystem. - Route: POST /api/v1/expenses/export/pdf streams binary application/pdf with cache-control:no-store. Validator caps expenseIds at 1000 to prevent runaway loops. Receipt-less expense flow (per user request): - Schema: 0033 migration adds `expenses.no_receipt_acknowledged` boolean (default false). - Validator: createExpenseSchema requires either receiptFileIds OR noReceiptAcknowledged=true; the .refine() error message tells the rep exactly what to do. updateExpenseSchema is partial and skips the rule (existing rows can be edited without re-acknowledging). - PDF: receiptless expenses get an inline red "(no receipt)" tag in the establishment cell + a red footer warning in the summary box showing the count and at-risk amount. - The legacy parent-company reimbursement queue may refuse to pay receiptless expenses, so the warning is load-bearing for ops. Audit-3 fixes piggy-backed: - 🔴 Tesseract OCR runtime now races a 30s timeout (CPU-bomb DoS protection — a crafted PDF rasterizing to high-res noise could pin the worker indefinitely). - 🟠 brochures.service.ts:listBrochures dropped a wasted query (the legacy single-brochure fast-path was discarding its result on the multi-brochure branch). - 🟠 berth-pdf.service.ts:listBerthPdfVersions now Promise.all's the presignDownload calls instead of awaiting each in a for-loop — 20-version berths went from 20× round-trip to 1×. - 🟡 public berths route no longer logs the full `row` object on enum drift (was dumping price + amenity columns into ops logs). - 🟡 dropped the dead `void sql` import from public berths route. Tests still 1163/1163. tsc clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 04:38:32 +02:00
"sharp": "^0.34.5",
"socket.io": "^4.8.3",
"socket.io-client": "^4.8.3",
fix(types): unblock catch-all routes under stricter Next 15.5 typing + Phase 2B deps Two changes bundled (build was failing on the type fix; deps came along on the same branch). 1. RouteHandler / withAuth / withPermission are now generic over the route's params shape. Default stays `Record<string, string>` for the common `[id]`-style routes (no caller changes needed). Catch-all routes like `[...path]` declare their narrow shape via a type-arg: export const PATCH = withAuth<{ path: string[] }>( withPermission<{ path: string[] }>('files', 'manage_folders', async (req, ctx, params) => { /* params.path: string[] */ } ), ); Without this, Next.js 15.5+'s stricter route-type checking rejected the build because the inferred `params: Promise<{ path: string[] }>` for `[...path]` doesn't satisfy `Promise<Record<string, string>>`. Updated `src/app/api/v1/files/folders/[...path]/route.ts` (the only catch-all in the tree right now) to use the new generic. 2. Phase 2B deps (within-major-jump where the API didn't actually break): - @pdfme/common, @pdfme/generator, @pdfme/schemas: 5.5.10 → 6.1.2 (closes 3 mod XSS/SSRF/decompression-bomb advisories) - lucide-react: 0.460.0 → 1.14.0 - sonner: 1.7.4 → 2.0.7 - tailwind-merge: 2.6.1 → 3.5.0 Tests: 1185/1185 vitest. tsc clean. Local `next build` succeeds. Reverted (deferred to a focused PR): - @hookform/resolvers 5: Resolver<T> typing change requires per-form useForm migration - eslint 10: incompatible with @rushstack/eslint-patch (pulled in by eslint-config-next) - react-day-picker 10: ClassNames removed `table`; needs calendar.tsx migration - zod 4: 94 type errors cascading through drizzle insert types; needs comprehensive migration Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 17:07:07 +02:00
"sonner": "^2.0.7",
deps: bump Tier-A patches + react-day-picker 10 + esbuild 0.28 Successfully bumped: - bullmq 5.76.6 → 5.76.8 - @tanstack/react-query 5.100.9 → 5.100.10 - @tanstack/react-query-devtools 5.100.9 → 5.100.10 - better-auth 1.6.9 → 1.6.10 - @playwright/test 1.59.1 → 1.60.0 - libphonenumber-js 1.12.43 → 1.13.1 - tailwind-merge 3.5.0 → 3.6.0 - vitest 4.1.5 → 4.1.6 - @vitest/coverage-v8 4.1.5 → 4.1.6 - lint-staged 17.0.3 → 17.0.4 - esbuild 0.27.7 → 0.28.0 - react-grab 0.1.33 → 0.1.34 - react-day-picker 9.14.0 → 10.0.0 react-day-picker 10 verified safe: probed v10 release notes against src/components/ui/calendar.tsx — we use only v9-canonical APIs that v10 preserves. Removed the `table` className entry from the wrapper (v10 dropped it since the renderer is now CSS-grid, not table-based). Tried + rolled back: - @hookform/resolvers 3 → 5: stricter input/output inference broke every form using <{schema}, any, {schema}> implicit shape. Needs per-form refactor; parked. Verified clean: pnpm audit (prod + dev) = 0 vulnerabilities; pnpm exec tsc --noEmit clean; vitest 1293/1293 pass. Remaining outdated (deliberately deferred — see docs/AUDIT-2026-05-12.md §34): - next/eslint-config-next 15 → 16 (2-4 wk wait) - zod 3 → 4 (couple with @hookform/resolvers 5; codemod-needed) - tailwindcss 3 → 4 (focused-afternoon project) - @types/node ^20.19 stays pinned to match runtime (audit decision) - archiver 7 stays (no @types/archiver@8 published) - eslint 9 stays (locked to eslint-config-next 15) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 17:33:24 +02:00
"tailwind-merge": "^3.6.0",
"tailwindcss-animate": "^1.0.7",
"tesseract.js": "^7.0.0",
"ts-pattern": "^5.9.0",
"vaul": "^1.1.2",
"web-vitals": "^5.2.0",
feat(deps): bump zod 3→4 + @hookform/resolvers 3→5 Resolved 65 type errors across the codebase via these v4 migration patterns: - `ZodError.errors` renamed to `ZodError.issues` (4 call sites in auth routes + central error handler). - `z.record(value)` now requires explicit key type: `z.record(z.string(), value)`. Updated 7 sites across templates / forms / saved-views / website-inquiries. - `.refine(check, msgFn)` second-arg shape changed — now requires an `{ error: (issue) => ... }` object form. Updated `mergeFieldsSchema` in document-templates validator. - `.transform(...).default(...)` chains: v4 enforces default value type matches transform OUTPUT. Reordered to `.default(...).transform(...)` in list-query / company-memberships handlers. - `z.coerce.*()` INPUT type widened to `unknown` in v4. Service signatures using `z.input<typeof schema>` (kept for caller flexibility around defaults) now re-parse via `schema.parse(data)` to recover the post-coercion shape Drizzle needs. Done in berth-reservations service. Invoice service narrows `lineItems` locally with a typed cast since re-parsing would double-validate. - `.optional().transform(...)` no longer propagates the optional marker through v4's new ZodPipe. Moved `.optional()` to the END of chain in `optionalDesiredDimSchema` (interests) and documents list query (folderId, signatureOnly). - ZodIssue subtype shapes simplified: `received` removed from invalid_type, `type` renamed to `origin` on too_small. Test fixtures updated. - @hookform/resolvers v5 splits Resolver into 3-generic form (Input, Context, Output). useForm calls in 6 forms (client, yacht, berth, interest, expense, invoices-new-page) now pass explicit generics: `useForm<z.input<typeof schema>, unknown, z.infer<typeof schema>>`. Verified: tsc clean (0 errors), vitest 1293/1293 pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 18:29:03 +02:00
"zod": "^4.4.3",
"zustand": "^5.0.13"
},
"devDependencies": {
"@eslint/eslintrc": "^3.3.5",
"@hookform/devtools": "^4.4.0",
"@next/bundle-analyzer": "^16.2.6",
deps: bump Tier-A patches + react-day-picker 10 + esbuild 0.28 Successfully bumped: - bullmq 5.76.6 → 5.76.8 - @tanstack/react-query 5.100.9 → 5.100.10 - @tanstack/react-query-devtools 5.100.9 → 5.100.10 - better-auth 1.6.9 → 1.6.10 - @playwright/test 1.59.1 → 1.60.0 - libphonenumber-js 1.12.43 → 1.13.1 - tailwind-merge 3.5.0 → 3.6.0 - vitest 4.1.5 → 4.1.6 - @vitest/coverage-v8 4.1.5 → 4.1.6 - lint-staged 17.0.3 → 17.0.4 - esbuild 0.27.7 → 0.28.0 - react-grab 0.1.33 → 0.1.34 - react-day-picker 9.14.0 → 10.0.0 react-day-picker 10 verified safe: probed v10 release notes against src/components/ui/calendar.tsx — we use only v9-canonical APIs that v10 preserves. Removed the `table` className entry from the wrapper (v10 dropped it since the renderer is now CSS-grid, not table-based). Tried + rolled back: - @hookform/resolvers 3 → 5: stricter input/output inference broke every form using <{schema}, any, {schema}> implicit shape. Needs per-form refactor; parked. Verified clean: pnpm audit (prod + dev) = 0 vulnerabilities; pnpm exec tsc --noEmit clean; vitest 1293/1293 pass. Remaining outdated (deliberately deferred — see docs/AUDIT-2026-05-12.md §34): - next/eslint-config-next 15 → 16 (2-4 wk wait) - zod 3 → 4 (couple with @hookform/resolvers 5; codemod-needed) - tailwindcss 3 → 4 (focused-afternoon project) - @types/node ^20.19 stays pinned to match runtime (audit decision) - archiver 7 stays (no @types/archiver@8 published) - eslint 9 stays (locked to eslint-config-next 15) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 17:33:24 +02:00
"@playwright/test": "^1.60.0",
"@total-typescript/ts-reset": "^0.6.1",
feat(gdpr): staff-triggered client-data export bundle (Article 15) Adds a full GDPR Article 15 (right of access) workflow. Staff trigger an export from the client detail; a BullMQ worker assembles every row keyed to that client (profile, contacts, addresses, notes, tags, yachts, company memberships, interests, reservations, invoices, documents, last 500 audit events) into JSON + a self-contained HTML report, ZIPs them, uploads to MinIO, and optionally emails the client a 7-day signed download link. - New table gdpr_exports tracks lifecycle (pending → building → ready → sent / failed) with a 30-day cleanup target - Bundle builder (gdpr-bundle-builder.ts) — pure read-side, tenant- scoped, with HTML escaping to block injection from rogue field values - Worker hook in export queue dispatches on job name 'gdpr-export' - New audit actions: 'request_gdpr_export', 'send_gdpr_export' - API: POST/GET /api/v1/clients/:id/gdpr-export (admin-gated, exports rate-limit, Article-15 audit on POST); GET /:exportId returns a fresh signed URL - UI: <GdprExportButton> dialog on client detail header — admin-only, shows recent exports, supports email-to-client + override recipient, polls every 5s while open - Validation: refuses email-to-client when no primary email + no override (rather than silently dropping the send) Tests: 778/778 vitest (was 771) — +7 covering builder happy path, HTML escaping, tenant isolation, empty client, request-flow validation, and audit / queue interaction. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 20:06:31 +02:00
"@types/archiver": "^7.0.0",
feat(i18n): country/phone/timezone/subdivision primitives + form wiring Cross-cutting i18n polish for forms across the marina + residential + company domains. Introduces a single source of truth for country/phone/timezone/ subdivision data and replaces every nationality-as-free-text and timezone- as-string Input with a dedicated combobox. PR1 Countries — ALL_COUNTRY_CODES (~250 ISO-3166-1 alpha-2), Intl.DisplayNames for localized labels, detectDefaultCountry() with navigator-region fallback to US, CountryCombobox with regional-indicator flag glyphs + compact mode for inline use. PR2 Phone — libphonenumber-js wrapper (parsePhone / formatAsYouType / callingCodeFor), PhoneInput with flag dropdown + national-format AsYouType + paste-detect that flips the country dropdown for pasted international strings. PR3 Timezones — country->IANA map (250 entries, multi-zone for AU/BR/CA/CD/ ID/KZ/MN/MX/RU/US), formatTimezoneLabel ("Europe/London (UTC+1)"), TimezoneCombobox with Suggested/All grouping driven by countryHint. PR4 Subdivisions — wraps the iso-3166-2 npm package (~5000 ISO 3166-2 codes for every country), per-country cache, SubdivisionCombobox with "Pick a country first" / "No regions available" empty states. PR5 Schema deltas (migration 0015) — clients.nationality_iso, clientContacts {value_e164, value_country}, clientAddresses {country_iso, subdivision_iso}, residentialClients {phone_e164, phone_country, nationality_iso, timezone, place_of_residence_country_iso, subdivision_iso}, companies {incorporation_ country_iso, incorporation_subdivision_iso}, companyAddresses {country_iso, subdivision_iso}. Plus shared zod validators (validators/i18n.ts) used by every entity validator + route handler. PR6 ClientForm + ClientDetail — CountryCombobox replaces nationality Input, TimezoneCombobox replaces timezone Input (driven by nationalityIso hint), PhoneInput conditionally rendered for phone/whatsapp contacts. Inline editors (InlineCountryField / InlineTimezoneField / InlinePhoneField) for the detail-page overview rows + ContactsEditor. PR7 Residential client form + detail — phone -> PhoneInput, nationality/ timezone/place-of-residence-country/subdivision rows in both create sheet and inline-editable detail view. Subdivision wipes when country flips since codes are country-scoped. PR8 Company form + detail — incorporation country -> CountryCombobox, incorporation region -> SubdivisionCombobox in both modes. PR9 Public inquiry endpoint — accepts pre-normalized phoneE164/phoneCountry and i18n fields from newer website builds, server-side parsePhone() fallback for legacy raw-international submissions. Old Nuxt builds keep working unchanged. Tests: 4 unit suites for the primitives (25 tests), 1 integration spec for the public phone-normalization path (3 tests), 1 smoke spec asserting the combobox triggers render in all three create sheets. Test totals: vitest 713 -> 741 (+28). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 18:13:08 +02:00
"@types/iso-3166-2": "^1.0.4",
"@types/mailparser": "^3.4.6",
"@types/node": "^20.19.0",
"@types/nodemailer": "^8.0.0",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^6.0.1",
deps: bump Tier-A patches + react-day-picker 10 + esbuild 0.28 Successfully bumped: - bullmq 5.76.6 → 5.76.8 - @tanstack/react-query 5.100.9 → 5.100.10 - @tanstack/react-query-devtools 5.100.9 → 5.100.10 - better-auth 1.6.9 → 1.6.10 - @playwright/test 1.59.1 → 1.60.0 - libphonenumber-js 1.12.43 → 1.13.1 - tailwind-merge 3.5.0 → 3.6.0 - vitest 4.1.5 → 4.1.6 - @vitest/coverage-v8 4.1.5 → 4.1.6 - lint-staged 17.0.3 → 17.0.4 - esbuild 0.27.7 → 0.28.0 - react-grab 0.1.33 → 0.1.34 - react-day-picker 9.14.0 → 10.0.0 react-day-picker 10 verified safe: probed v10 release notes against src/components/ui/calendar.tsx — we use only v9-canonical APIs that v10 preserves. Removed the `table` className entry from the wrapper (v10 dropped it since the renderer is now CSS-grid, not table-based). Tried + rolled back: - @hookform/resolvers 3 → 5: stricter input/output inference broke every form using <{schema}, any, {schema}> implicit shape. Needs per-form refactor; parked. Verified clean: pnpm audit (prod + dev) = 0 vulnerabilities; pnpm exec tsc --noEmit clean; vitest 1293/1293 pass. Remaining outdated (deliberately deferred — see docs/AUDIT-2026-05-12.md §34): - next/eslint-config-next 15 → 16 (2-4 wk wait) - zod 3 → 4 (couple with @hookform/resolvers 5; codemod-needed) - tailwindcss 3 → 4 (focused-afternoon project) - @types/node ^20.19 stays pinned to match runtime (audit decision) - archiver 7 stays (no @types/archiver@8 published) - eslint 9 stays (locked to eslint-config-next 15) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 17:33:24 +02:00
"@vitest/coverage-v8": "^4.1.6",
"autoprefixer": "^10.5.0",
"dotenv": "^17.4.2",
"drizzle-kit": "^0.31.10",
feat(deps): bump zod 3→4 + @hookform/resolvers 3→5 Resolved 65 type errors across the codebase via these v4 migration patterns: - `ZodError.errors` renamed to `ZodError.issues` (4 call sites in auth routes + central error handler). - `z.record(value)` now requires explicit key type: `z.record(z.string(), value)`. Updated 7 sites across templates / forms / saved-views / website-inquiries. - `.refine(check, msgFn)` second-arg shape changed — now requires an `{ error: (issue) => ... }` object form. Updated `mergeFieldsSchema` in document-templates validator. - `.transform(...).default(...)` chains: v4 enforces default value type matches transform OUTPUT. Reordered to `.default(...).transform(...)` in list-query / company-memberships handlers. - `z.coerce.*()` INPUT type widened to `unknown` in v4. Service signatures using `z.input<typeof schema>` (kept for caller flexibility around defaults) now re-parse via `schema.parse(data)` to recover the post-coercion shape Drizzle needs. Done in berth-reservations service. Invoice service narrows `lineItems` locally with a typed cast since re-parsing would double-validate. - `.optional().transform(...)` no longer propagates the optional marker through v4's new ZodPipe. Moved `.optional()` to the END of chain in `optionalDesiredDimSchema` (interests) and documents list query (folderId, signatureOnly). - ZodIssue subtype shapes simplified: `received` removed from invalid_type, `type` renamed to `origin` on too_small. Test fixtures updated. - @hookform/resolvers v5 splits Resolver into 3-generic form (Input, Context, Output). useForm calls in 6 forms (client, yacht, berth, interest, expense, invoices-new-page) now pass explicit generics: `useForm<z.input<typeof schema>, unknown, z.infer<typeof schema>>`. Verified: tsc clean (0 errors), vitest 1293/1293 pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 18:29:03 +02:00
"drizzle-zod": "^0.8.3",
deps: bump Tier-A patches + react-day-picker 10 + esbuild 0.28 Successfully bumped: - bullmq 5.76.6 → 5.76.8 - @tanstack/react-query 5.100.9 → 5.100.10 - @tanstack/react-query-devtools 5.100.9 → 5.100.10 - better-auth 1.6.9 → 1.6.10 - @playwright/test 1.59.1 → 1.60.0 - libphonenumber-js 1.12.43 → 1.13.1 - tailwind-merge 3.5.0 → 3.6.0 - vitest 4.1.5 → 4.1.6 - @vitest/coverage-v8 4.1.5 → 4.1.6 - lint-staged 17.0.3 → 17.0.4 - esbuild 0.27.7 → 0.28.0 - react-grab 0.1.33 → 0.1.34 - react-day-picker 9.14.0 → 10.0.0 react-day-picker 10 verified safe: probed v10 release notes against src/components/ui/calendar.tsx — we use only v9-canonical APIs that v10 preserves. Removed the `table` className entry from the wrapper (v10 dropped it since the renderer is now CSS-grid, not table-based). Tried + rolled back: - @hookform/resolvers 3 → 5: stricter input/output inference broke every form using <{schema}, any, {schema}> implicit shape. Needs per-form refactor; parked. Verified clean: pnpm audit (prod + dev) = 0 vulnerabilities; pnpm exec tsc --noEmit clean; vitest 1293/1293 pass. Remaining outdated (deliberately deferred — see docs/AUDIT-2026-05-12.md §34): - next/eslint-config-next 15 → 16 (2-4 wk wait) - zod 3 → 4 (couple with @hookform/resolvers 5; codemod-needed) - tailwindcss 3 → 4 (focused-afternoon project) - @types/node ^20.19 stays pinned to match runtime (audit decision) - archiver 7 stays (no @types/archiver@8 published) - eslint 9 stays (locked to eslint-config-next 15) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 17:33:24 +02:00
"esbuild": "^0.28.0",
"eslint": "^9.39.4",
"eslint-config-next": "15.5.18",
"eslint-config-prettier": "^10.1.8",
"husky": "^9.1.7",
deps: bump Tier-A patches + react-day-picker 10 + esbuild 0.28 Successfully bumped: - bullmq 5.76.6 → 5.76.8 - @tanstack/react-query 5.100.9 → 5.100.10 - @tanstack/react-query-devtools 5.100.9 → 5.100.10 - better-auth 1.6.9 → 1.6.10 - @playwright/test 1.59.1 → 1.60.0 - libphonenumber-js 1.12.43 → 1.13.1 - tailwind-merge 3.5.0 → 3.6.0 - vitest 4.1.5 → 4.1.6 - @vitest/coverage-v8 4.1.5 → 4.1.6 - lint-staged 17.0.3 → 17.0.4 - esbuild 0.27.7 → 0.28.0 - react-grab 0.1.33 → 0.1.34 - react-day-picker 9.14.0 → 10.0.0 react-day-picker 10 verified safe: probed v10 release notes against src/components/ui/calendar.tsx — we use only v9-canonical APIs that v10 preserves. Removed the `table` className entry from the wrapper (v10 dropped it since the renderer is now CSS-grid, not table-based). Tried + rolled back: - @hookform/resolvers 3 → 5: stricter input/output inference broke every form using <{schema}, any, {schema}> implicit shape. Needs per-form refactor; parked. Verified clean: pnpm audit (prod + dev) = 0 vulnerabilities; pnpm exec tsc --noEmit clean; vitest 1293/1293 pass. Remaining outdated (deliberately deferred — see docs/AUDIT-2026-05-12.md §34): - next/eslint-config-next 15 → 16 (2-4 wk wait) - zod 3 → 4 (couple with @hookform/resolvers 5; codemod-needed) - tailwindcss 3 → 4 (focused-afternoon project) - @types/node ^20.19 stays pinned to match runtime (audit decision) - archiver 7 stays (no @types/archiver@8 published) - eslint 9 stays (locked to eslint-config-next 15) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 17:33:24 +02:00
"lint-staged": "^17.0.4",
"postcss": "^8.5.14",
"prettier": "^3.8.3",
deps: bump Tier-A patches + react-day-picker 10 + esbuild 0.28 Successfully bumped: - bullmq 5.76.6 → 5.76.8 - @tanstack/react-query 5.100.9 → 5.100.10 - @tanstack/react-query-devtools 5.100.9 → 5.100.10 - better-auth 1.6.9 → 1.6.10 - @playwright/test 1.59.1 → 1.60.0 - libphonenumber-js 1.12.43 → 1.13.1 - tailwind-merge 3.5.0 → 3.6.0 - vitest 4.1.5 → 4.1.6 - @vitest/coverage-v8 4.1.5 → 4.1.6 - lint-staged 17.0.3 → 17.0.4 - esbuild 0.27.7 → 0.28.0 - react-grab 0.1.33 → 0.1.34 - react-day-picker 9.14.0 → 10.0.0 react-day-picker 10 verified safe: probed v10 release notes against src/components/ui/calendar.tsx — we use only v9-canonical APIs that v10 preserves. Removed the `table` className entry from the wrapper (v10 dropped it since the renderer is now CSS-grid, not table-based). Tried + rolled back: - @hookform/resolvers 3 → 5: stricter input/output inference broke every form using <{schema}, any, {schema}> implicit shape. Needs per-form refactor; parked. Verified clean: pnpm audit (prod + dev) = 0 vulnerabilities; pnpm exec tsc --noEmit clean; vitest 1293/1293 pass. Remaining outdated (deliberately deferred — see docs/AUDIT-2026-05-12.md §34): - next/eslint-config-next 15 → 16 (2-4 wk wait) - zod 3 → 4 (couple with @hookform/resolvers 5; codemod-needed) - tailwindcss 3 → 4 (focused-afternoon project) - @types/node ^20.19 stays pinned to match runtime (audit decision) - archiver 7 stays (no @types/archiver@8 published) - eslint 9 stays (locked to eslint-config-next 15) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 17:33:24 +02:00
"react-grab": "^0.1.34",
"tailwindcss": "^3.4.19",
"tsx": "^4.21.0",
"typescript": "^6.0.3",
deps: bump Tier-A patches + react-day-picker 10 + esbuild 0.28 Successfully bumped: - bullmq 5.76.6 → 5.76.8 - @tanstack/react-query 5.100.9 → 5.100.10 - @tanstack/react-query-devtools 5.100.9 → 5.100.10 - better-auth 1.6.9 → 1.6.10 - @playwright/test 1.59.1 → 1.60.0 - libphonenumber-js 1.12.43 → 1.13.1 - tailwind-merge 3.5.0 → 3.6.0 - vitest 4.1.5 → 4.1.6 - @vitest/coverage-v8 4.1.5 → 4.1.6 - lint-staged 17.0.3 → 17.0.4 - esbuild 0.27.7 → 0.28.0 - react-grab 0.1.33 → 0.1.34 - react-day-picker 9.14.0 → 10.0.0 react-day-picker 10 verified safe: probed v10 release notes against src/components/ui/calendar.tsx — we use only v9-canonical APIs that v10 preserves. Removed the `table` className entry from the wrapper (v10 dropped it since the renderer is now CSS-grid, not table-based). Tried + rolled back: - @hookform/resolvers 3 → 5: stricter input/output inference broke every form using <{schema}, any, {schema}> implicit shape. Needs per-form refactor; parked. Verified clean: pnpm audit (prod + dev) = 0 vulnerabilities; pnpm exec tsc --noEmit clean; vitest 1293/1293 pass. Remaining outdated (deliberately deferred — see docs/AUDIT-2026-05-12.md §34): - next/eslint-config-next 15 → 16 (2-4 wk wait) - zod 3 → 4 (couple with @hookform/resolvers 5; codemod-needed) - tailwindcss 3 → 4 (focused-afternoon project) - @types/node ^20.19 stays pinned to match runtime (audit decision) - archiver 7 stays (no @types/archiver@8 published) - eslint 9 stays (locked to eslint-config-next 15) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 17:33:24 +02:00
"vitest": "^4.1.6"
},
"pnpm": {
"overrides": {
"vite": "8.0.5",
"esbuild": ">=0.25.0",
"postcss": ">=8.5.10"
}
}
}