From 09ffe79bd03835723b34bf43870bc9b121fa5649 Mon Sep 17 00:00:00 2001 From: Chris Kehayias Date: Sat, 30 May 2026 16:08:52 -0400 Subject: [PATCH 01/20] feat: route MP datetime through DomainTimezoneService at the boundary MP stores and interprets datetime values as wall-clock in the domain's time zone, not UTC. Converting with raw new Date().toISOString() shifted date-boundary queries by the UTC offset. - Add DomainTimezoneService singleton (domain TZ -> IANA, toMpSqlDatetime, parseMpDatetime) with unit tests - Add getMpTimezone() server action for client-side Intl.DateTimeFormat rendering - Route fullCalendar getEvents \ literals through toMpSqlDatetime (+ regression tests) - Document the boundary-conversion rule and anti-patterns (CLAUDE.md, references, playbook) Co-Authored-By: Claude Opus 4.8 (1M context) --- .../playbooks/port-mp-datetime-handling.md | 986 ++++++++++++++++++ .../ministryplatform.datetimehandling.md | 134 +++ CLAUDE.md | 11 +- next.config.ts | 14 +- src/app/actions/domain.ts | 16 + .../ministry-platform/docs/README.md | 7 +- src/lib/providers/ministry-platform/helper.ts | 6 +- src/services/domainTimezoneService.test.ts | 154 +++ src/services/domainTimezoneService.ts | 344 ++++++ src/services/fullCalendarService.test.ts | 52 +- src/services/fullCalendarService.ts | 10 +- 11 files changed, 1722 insertions(+), 12 deletions(-) create mode 100644 .claude/playbooks/port-mp-datetime-handling.md create mode 100644 .claude/references/ministryplatform.datetimehandling.md create mode 100644 src/app/actions/domain.ts create mode 100644 src/services/domainTimezoneService.test.ts create mode 100644 src/services/domainTimezoneService.ts diff --git a/.claude/playbooks/port-mp-datetime-handling.md b/.claude/playbooks/port-mp-datetime-handling.md new file mode 100644 index 0000000..e50aa1e --- /dev/null +++ b/.claude/playbooks/port-mp-datetime-handling.md @@ -0,0 +1,986 @@ +# Playbook: Port Ministry Platform Date/Time Handling Into This Repo + +You are Claude Code running in a repo that integrates with **Ministry Platform** (MP). Another team has solved a class of bugs where MP datetimes round-trip through UTC and silently drift by the server's-local-to-MP-timezone offset. This playbook ports that fix into the repo you're in. + +**Outcome you're driving toward** + +1. A `DomainTimezoneService` singleton exists in this repo and is the only path through which code reads `MPHelper.getDomainInfo().TimeZoneName`. +2. Every MP datetime write goes through `DomainTimezoneService.toMpSqlDatetime(...)`. No more `Date.toISOString()` / `T00:00:00.000Z` / `new Date(...).getFullYear()` patterns on the MP boundary. +3. Every MP datetime display uses `Intl.DateTimeFormat({ timeZone })` with the MP timezone, not browser-local parsing. +4. A reference doc `.claude/references/ministryplatform.datetimehandling.md` is checked in. +5. `CLAUDE.md` cites the reference and includes a Key Development Practice rule pointing to it. +6. Tests cover the service and at least one round-trip regression for a real datetime field, and pass under `TZ=UTC` and `TZ=America/Los_Angeles`. + +**Do not skip the discovery phase.** Paths, naming, and the exact MPHelper surface may differ from the source project. Confirm the assumptions before writing code. + +## Background — the bug class you're preventing + +MP stores datetimes as **wall-clock values in the domain's configured time zone** (exposed via `getDomainInfo().TimeZoneName`). It does **not** normalize to UTC. + +If a write path tags a value as UTC (e.g. appending `Z`) and then formats it with `new Date(...).getFullYear()` (which reads in the Node process's local zone), the SQL string sent to MP carries the server-local clock numbers for an instant that was tagged UTC. MP stores those numbers as if they were already in MP-TZ, so the record drifts by the server-to-MP offset. The mirror anti-pattern on the read path — `new Date(stringFromMp).toLocaleDateString(...)` — re-parses MP's wall-clock-in-MP-TZ as if it were browser-local, drifting again. When an edit form reads the already-shifted value and the write re-applies the transform, each edit shifts the date by another day. + +Concrete example from the source repo: a customer saved a Contact Log at 11:33 PM Eastern on 2026-05-17. It saved as 2026-05-16 at 8:00 PM. Editing without changing any field shifted the date back another day every time. + +The fix has three pieces: + +1. **A boundary service** that knows the MP timezone, returns it as IANA, and converts values without going through `Date.toISOString()` or `Date.getFullYear()`. +2. **A grep-and-fix pass** on every site that touches an MP date column — forms, server actions, services, display formatters. +3. **A reference doc + CLAUDE.md update** so future MP date work doesn't re-introduce the pattern. + +## Phase 1 — Discovery + +Before writing any code, answer these by reading the repo: + +1. **Where does `MPHelper` live, and what is its import path?** + - In the source repo: `@/lib/providers/ministry-platform` exporting `MPHelper`, with `getDomainInfo()` returning `{ TimeZoneName, DisplayName, CultureName, ... }`. + - In *this* repo it may be at a different path, named differently (e.g. `MinistryPlatformClient`), or expose domain info via a different method. + - Find it: `grep -r "getDomainInfo" src/` and inspect the type. Confirm it returns a `TimeZoneName` field. + - **Stop and ask the user** if `getDomainInfo` doesn't exist or doesn't surface a time zone. Don't invent it. + +2. **Where do services live?** (e.g. `src/services/`, `src/lib/services/`, `app/services/`). Match the existing convention. + +3. **Where do shared server actions live?** Some Next.js repos have `src/components/shared-actions/`, others have `src/app/actions/`, others have just `_actions.ts` files. Match the convention. + +4. **Which testing framework?** Vitest or Jest. The mock patterns differ slightly. The source repo uses Vitest with `vi.hoisted()` and mocks `MPHelper` as a class. Adapt as needed. + +5. **Path alias.** Most repos use `@/*` for `src/*`. Verify in `tsconfig.json`. + +6. **MP date fields in this repo.** Grep for anti-patterns (these are the bugs you'll fix): + ``` + grep -rn "T00:00:00.000Z" src/ + grep -rn "T00:00:00Z" src/ + grep -rn "\.toISOString()" src/ + grep -rn "new Date(.*)\.getFullYear()" src/ + grep -rn "new Date(.*)\.getMonth()" src/ + grep -rn "new Date(.*)\.toLocaleDateString" src/ + grep -rn "new Date(.*)\.toLocaleString" src/ + ``` + Also enumerate every component/server-action that writes a column ending in `_Date`, `_DateTime`, `_Time`, `Date`, or `Time` to MP. Common ones: `Contact_Date`, `Start_Date`, `End_Date`, `Birthdate`, `Donation_Date`, `Event_Start_Date`, `Pledge_Start_Date`. + +7. **Is there already a partial fix in place?** Search for `TimeZoneName`, `timezone`, `tz`, `getDomainInfo` to see if anyone has started this work. If so, don't duplicate — extend it. + +Write the answers down (you can use TaskCreate to track them). Only proceed to Phase 2 when each question has an answer or has been raised with the user. + +## Phase 2 — Drop in the service + +Create `src/services/domainTimezoneService.ts` (or wherever services live in this repo) with the contents below. Adjust the `MPHelper` import path if Phase 1 found it lives elsewhere. The file is self-contained — no other dependencies. + +```ts +import { MPHelper } from "@/lib/providers/ministry-platform"; + +/** + * Mapping of common Windows time zone IDs (as returned by the MP /domain endpoint's + * `TimeZoneName` field) to IANA time zone identifiers (which `Intl.DateTimeFormat` + * requires). Extend as new MP-hosted domains surface zones not listed here. + */ +const WINDOWS_TO_IANA: Record = { + "Dateline Standard Time": "Etc/GMT+12", + "UTC-11": "Etc/GMT+11", + "Aleutian Standard Time": "America/Adak", + "Hawaiian Standard Time": "Pacific/Honolulu", + "Marquesas Standard Time": "Pacific/Marquesas", + "Alaskan Standard Time": "America/Anchorage", + "UTC-09": "Etc/GMT+9", + "Pacific Standard Time (Mexico)": "America/Tijuana", + "UTC-08": "Etc/GMT+8", + "Pacific Standard Time": "America/Los_Angeles", + "US Mountain Standard Time": "America/Phoenix", + "Mountain Standard Time (Mexico)": "America/Mazatlan", + "Mountain Standard Time": "America/Denver", + "Central America Standard Time": "America/Guatemala", + "Central Standard Time": "America/Chicago", + "Easter Island Standard Time": "Pacific/Easter", + "Central Standard Time (Mexico)": "America/Mexico_City", + "Canada Central Standard Time": "America/Regina", + "SA Pacific Standard Time": "America/Bogota", + "Eastern Standard Time (Mexico)": "America/Cancun", + "Eastern Standard Time": "America/New_York", + "Haiti Standard Time": "America/Port-au-Prince", + "Cuba Standard Time": "America/Havana", + "US Eastern Standard Time": "America/Indianapolis", + "Turks And Caicos Standard Time": "America/Grand_Turk", + "Paraguay Standard Time": "America/Asuncion", + "Atlantic Standard Time": "America/Halifax", + "Venezuela Standard Time": "America/Caracas", + "Central Brazilian Standard Time": "America/Cuiaba", + "SA Western Standard Time": "America/La_Paz", + "Pacific SA Standard Time": "America/Santiago", + "Newfoundland Standard Time": "America/St_Johns", + "Tocantins Standard Time": "America/Araguaina", + "E. South America Standard Time": "America/Sao_Paulo", + "SA Eastern Standard Time": "America/Cayenne", + "Argentina Standard Time": "America/Buenos_Aires", + "Greenland Standard Time": "America/Godthab", + "Montevideo Standard Time": "America/Montevideo", + "Magallanes Standard Time": "America/Punta_Arenas", + "Saint Pierre Standard Time": "America/Miquelon", + "Bahia Standard Time": "America/Bahia", + "UTC-02": "Etc/GMT+2", + "Azores Standard Time": "Atlantic/Azores", + "Cape Verde Standard Time": "Atlantic/Cape_Verde", + UTC: "Etc/UTC", + "GMT Standard Time": "Europe/London", + "Greenwich Standard Time": "Atlantic/Reykjavik", + "Sao Tome Standard Time": "Africa/Sao_Tome", + "Morocco Standard Time": "Africa/Casablanca", + "W. Europe Standard Time": "Europe/Berlin", + "Central Europe Standard Time": "Europe/Budapest", + "Romance Standard Time": "Europe/Paris", + "Central European Standard Time": "Europe/Warsaw", + "W. Central Africa Standard Time": "Africa/Lagos", + "Jordan Standard Time": "Asia/Amman", + "GTB Standard Time": "Europe/Bucharest", + "Middle East Standard Time": "Asia/Beirut", + "Egypt Standard Time": "Africa/Cairo", + "E. Europe Standard Time": "Europe/Chisinau", + "Syria Standard Time": "Asia/Damascus", + "West Bank Standard Time": "Asia/Hebron", + "South Africa Standard Time": "Africa/Johannesburg", + "FLE Standard Time": "Europe/Kiev", + "Israel Standard Time": "Asia/Jerusalem", + "Kaliningrad Standard Time": "Europe/Kaliningrad", + "Sudan Standard Time": "Africa/Khartoum", + "Libya Standard Time": "Africa/Tripoli", + "Namibia Standard Time": "Africa/Windhoek", + "Arabic Standard Time": "Asia/Baghdad", + "Turkey Standard Time": "Europe/Istanbul", + "Arab Standard Time": "Asia/Riyadh", + "Belarus Standard Time": "Europe/Minsk", + "Russian Standard Time": "Europe/Moscow", + "E. Africa Standard Time": "Africa/Nairobi", + "Iran Standard Time": "Asia/Tehran", + "Arabian Standard Time": "Asia/Dubai", + "Astrakhan Standard Time": "Europe/Astrakhan", + "Azerbaijan Standard Time": "Asia/Baku", + "Russia Time Zone 3": "Europe/Samara", + "Mauritius Standard Time": "Indian/Mauritius", + "Saratov Standard Time": "Europe/Saratov", + "Georgian Standard Time": "Asia/Tbilisi", + "Volgograd Standard Time": "Europe/Volgograd", + "Caucasus Standard Time": "Asia/Yerevan", + "Afghanistan Standard Time": "Asia/Kabul", + "West Asia Standard Time": "Asia/Tashkent", + "Ekaterinburg Standard Time": "Asia/Yekaterinburg", + "Pakistan Standard Time": "Asia/Karachi", + "Qyzylorda Standard Time": "Asia/Qyzylorda", + "India Standard Time": "Asia/Calcutta", + "Sri Lanka Standard Time": "Asia/Colombo", + "Nepal Standard Time": "Asia/Katmandu", + "Central Asia Standard Time": "Asia/Almaty", + "Bangladesh Standard Time": "Asia/Dhaka", + "Omsk Standard Time": "Asia/Omsk", + "Myanmar Standard Time": "Asia/Rangoon", + "SE Asia Standard Time": "Asia/Bangkok", + "Altai Standard Time": "Asia/Barnaul", + "W. Mongolia Standard Time": "Asia/Hovd", + "North Asia Standard Time": "Asia/Krasnoyarsk", + "N. Central Asia Standard Time": "Asia/Novosibirsk", + "Tomsk Standard Time": "Asia/Tomsk", + "China Standard Time": "Asia/Shanghai", + "North Asia East Standard Time": "Asia/Irkutsk", + "Singapore Standard Time": "Asia/Singapore", + "W. Australia Standard Time": "Australia/Perth", + "Taipei Standard Time": "Asia/Taipei", + "Ulaanbaatar Standard Time": "Asia/Ulaanbaatar", + "Aus Central W. Standard Time": "Australia/Eucla", + "Transbaikal Standard Time": "Asia/Chita", + "Tokyo Standard Time": "Asia/Tokyo", + "North Korea Standard Time": "Asia/Pyongyang", + "Korea Standard Time": "Asia/Seoul", + "Yakutsk Standard Time": "Asia/Yakutsk", + "Cen. Australia Standard Time": "Australia/Adelaide", + "AUS Central Standard Time": "Australia/Darwin", + "E. Australia Standard Time": "Australia/Brisbane", + "AUS Eastern Standard Time": "Australia/Sydney", + "West Pacific Standard Time": "Pacific/Port_Moresby", + "Tasmania Standard Time": "Australia/Hobart", + "Vladivostok Standard Time": "Asia/Vladivostok", + "Lord Howe Standard Time": "Australia/Lord_Howe", + "Bougainville Standard Time": "Pacific/Bougainville", + "Russia Time Zone 10": "Asia/Srednekolymsk", + "Magadan Standard Time": "Asia/Magadan", + "Norfolk Standard Time": "Pacific/Norfolk", + "Sakhalin Standard Time": "Asia/Sakhalin", + "Central Pacific Standard Time": "Pacific/Guadalcanal", + "Russia Time Zone 11": "Asia/Kamchatka", + "New Zealand Standard Time": "Pacific/Auckland", + "UTC+12": "Etc/GMT-12", + "Fiji Standard Time": "Pacific/Fiji", + "Chatham Islands Standard Time": "Pacific/Chatham", + "UTC+13": "Etc/GMT-13", + "Tonga Standard Time": "Pacific/Tongatapu", + "Samoa Standard Time": "Pacific/Apia", + "Line Islands Standard Time": "Pacific/Kiritimati", +}; + +/** + * Resolves an MP-provided time zone identifier to an IANA name. Accepts either a + * Windows zone (MP's typical output, e.g. "Eastern Standard Time") or an IANA + * name already (e.g. "America/New_York"). Throws if the value is unknown so + * callers fail fast rather than silently drift to the server's local zone. + */ +export function resolveIanaTimezone(timeZone: string): string { + if (!timeZone || typeof timeZone !== "string") { + throw new Error("Time zone identifier is required"); + } + const trimmed = timeZone.trim(); + if (trimmed.length === 0) { + throw new Error("Time zone identifier is required"); + } + if (trimmed === "UTC" || trimmed === "Etc/UTC") { + return "Etc/UTC"; + } + if (trimmed.includes("/")) { + return trimmed; + } + const mapped = WINDOWS_TO_IANA[trimmed]; + if (!mapped) { + throw new Error( + `Unknown time zone "${trimmed}" — add it to the Windows→IANA mapping in domainTimezoneService.ts` + ); + } + return mapped; +} + +function parseWallClockParts(value: string): { + year: number; + month: number; + day: number; + hour: number; + minute: number; + second: number; +} | null { + const trimmed = value.trim(); + if (/Z$/.test(trimmed) || /[+-]\d{2}:?\d{2}$/.test(trimmed)) { + return null; + } + const match = trimmed.match( + /^(\d{4})-(\d{2})-(\d{2})(?:[T ](\d{2}):(\d{2})(?::(\d{2}))?(?:\.\d+)?)?$/ + ); + if (!match) { + return null; + } + const [, y, mo, d, h = "00", mi = "00", s = "00"] = match; + return { + year: Number(y), + month: Number(mo), + day: Number(d), + hour: Number(h), + minute: Number(mi), + second: Number(s), + }; +} + +function formatInstantAsMpSql(instant: Date, ianaTimeZone: string): string { + const parts = new Intl.DateTimeFormat("en-CA", { + timeZone: ianaTimeZone, + year: "numeric", + month: "2-digit", + day: "2-digit", + hour: "2-digit", + minute: "2-digit", + second: "2-digit", + hour12: false, + }).formatToParts(instant); + const lookup: Record = {}; + for (const part of parts) { + lookup[part.type] = part.value; + } + // Some ICU builds emit "24" for midnight under hour12:false; normalize. + const hour = lookup.hour === "24" ? "00" : lookup.hour; + return `${lookup.year}-${lookup.month}-${lookup.day} ${hour}:${lookup.minute}:${lookup.second}`; +} + +/** + * DomainTimezoneService — singleton helper for converting date/time values + * between MP's domain time zone and the application's various surfaces. + * + * Why this exists: MP stores datetimes as wall-clock values in the domain's + * configured time zone (NOT UTC). Sending a UTC-tagged value or letting + * `new Date(...).getFullYear()` round-trip through the server's local time + * silently shifts dates by the offset between server and MP. + */ +export class DomainTimezoneService { + private static instance: DomainTimezoneService | null = null; + private mp: MPHelper; + private cachedIana: string | null = null; + private inflight: Promise | null = null; + + private constructor() { + this.mp = new MPHelper(); + } + + public static getInstance(): DomainTimezoneService { + if (!DomainTimezoneService.instance) { + DomainTimezoneService.instance = new DomainTimezoneService(); + } + return DomainTimezoneService.instance; + } + + public async getMpTimezone(): Promise { + if (this.cachedIana) { + return this.cachedIana; + } + if (!this.inflight) { + this.inflight = (async () => { + const info = await this.mp.getDomainInfo(); + const iana = resolveIanaTimezone(info.TimeZoneName); + this.cachedIana = iana; + return iana; + })().finally(() => { + this.inflight = null; + }); + } + return this.inflight; + } + + /** + * Converts a value into the SQL datetime string MP's table API expects + * ("YYYY-MM-DD HH:MM:SS" in the MP domain's wall-clock time). + * + * - Wall-clock string with no zone marker → reformatted as MP-TZ wall-clock, + * missing components default to zero. + * - String with trailing "Z" or "±HH:MM" offset → parsed as a UTC/offset + * instant and converted into MP-TZ wall-clock. + * - `Date` instances → converted as UTC instants. + */ + public async toMpSqlDatetime(value: Date | string): Promise { + if (value instanceof Date) { + const iana = await this.getMpTimezone(); + return formatInstantAsMpSql(value, iana); + } + if (typeof value !== "string" || value.trim().length === 0) { + throw new Error("toMpSqlDatetime: value must be a non-empty string or Date"); + } + const wallClock = parseWallClockParts(value); + if (wallClock) { + const pad = (n: number) => String(n).padStart(2, "0"); + return `${wallClock.year}-${pad(wallClock.month)}-${pad(wallClock.day)} ${pad(wallClock.hour)}:${pad(wallClock.minute)}:${pad(wallClock.second)}`; + } + const parsed = new Date(value); + if (Number.isNaN(parsed.getTime())) { + throw new Error(`toMpSqlDatetime: unable to parse "${value}"`); + } + const iana = await this.getMpTimezone(); + return formatInstantAsMpSql(parsed, iana); + } + + /** + * Parses an MP wall-clock datetime string into a `Date` instant. Use when + * you need real arithmetic on values returned from MP — for display, prefer + * `Intl.DateTimeFormat({ timeZone })` directly against the raw string. + */ + public async parseMpDatetime(value: string): Promise { + const wallClock = parseWallClockParts(value); + if (!wallClock) { + const direct = new Date(value); + if (Number.isNaN(direct.getTime())) { + throw new Error(`parseMpDatetime: unable to parse "${value}"`); + } + return direct; + } + const iana = await this.getMpTimezone(); + const utcGuess = Date.UTC( + wallClock.year, + wallClock.month - 1, + wallClock.day, + wallClock.hour, + wallClock.minute, + wallClock.second + ); + const projected = formatInstantAsMpSql(new Date(utcGuess), iana); + const projectedParts = parseWallClockParts(projected)!; + const projectedUtc = Date.UTC( + projectedParts.year, + projectedParts.month - 1, + projectedParts.day, + projectedParts.hour, + projectedParts.minute, + projectedParts.second + ); + const offset = utcGuess - projectedUtc; + return new Date(utcGuess + offset); + } + + /** Test hook — clears cached domain info so the next call refetches. */ + public clearCache(): void { + this.cachedIana = null; + this.inflight = null; + } +} + +export const domainTimezoneService = DomainTimezoneService.getInstance(); +``` + +## Phase 3 — Add the service tests + +Create `src/services/domainTimezoneService.test.ts` next to the service. The mock pattern matters: this repo's singleton is constructed at module load, so the `MPHelper` mock must be set up before the import, which requires `vi.hoisted()` under Vitest. Under Jest, use `jest.mock` factory with the variable referenced at the bottom of the file or via `jest.requireActual` patterns — adapt to local conventions. + +```ts +import { describe, it, expect, vi, beforeEach } from "vitest"; + +const { mockGetDomainInfo } = vi.hoisted(() => ({ + mockGetDomainInfo: vi.fn(), +})); + +vi.mock("@/lib/providers/ministry-platform", () => { + return { + MPHelper: class { + getDomainInfo = mockGetDomainInfo; + }, + }; +}); + +import { + DomainTimezoneService, + resolveIanaTimezone, +} from "@/services/domainTimezoneService"; + +function freshService(): DomainTimezoneService { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (DomainTimezoneService as any).instance = null; + return DomainTimezoneService.getInstance(); +} + +describe("resolveIanaTimezone", () => { + it("maps common Windows zone names to IANA", () => { + expect(resolveIanaTimezone("Eastern Standard Time")).toBe("America/New_York"); + expect(resolveIanaTimezone("Central Standard Time")).toBe("America/Chicago"); + expect(resolveIanaTimezone("Pacific Standard Time")).toBe("America/Los_Angeles"); + expect(resolveIanaTimezone("GMT Standard Time")).toBe("Europe/London"); + }); + + it("passes through IANA zone names unchanged", () => { + expect(resolveIanaTimezone("America/Chicago")).toBe("America/Chicago"); + expect(resolveIanaTimezone("Europe/Berlin")).toBe("Europe/Berlin"); + }); + + it("normalizes UTC variants", () => { + expect(resolveIanaTimezone("UTC")).toBe("Etc/UTC"); + expect(resolveIanaTimezone("Etc/UTC")).toBe("Etc/UTC"); + }); + + it("throws for unknown identifiers rather than silently falling back", () => { + expect(() => resolveIanaTimezone("Atlantis Standard Time")).toThrow(/Unknown time zone/); + expect(() => resolveIanaTimezone("")).toThrow(); + }); +}); + +describe("DomainTimezoneService", () => { + beforeEach(() => { + // Use mockReset (not clearAllMocks) so mockResolvedValueOnce queues are + // drained between tests. Date-only paths skip getMpTimezone() and would + // otherwise leak unconsumed queue entries forward. + mockGetDomainInfo.mockReset(); + }); + + describe("getMpTimezone", () => { + it("fetches and caches the IANA zone after first call", async () => { + mockGetDomainInfo.mockResolvedValueOnce({ + TimeZoneName: "Eastern Standard Time", + DisplayName: "Test", + CultureName: "en-US", + }); + const svc = freshService(); + expect(await svc.getMpTimezone()).toBe("America/New_York"); + expect(await svc.getMpTimezone()).toBe("America/New_York"); + expect(mockGetDomainInfo).toHaveBeenCalledTimes(1); + }); + + it("accepts an IANA zone from MP without mapping", async () => { + mockGetDomainInfo.mockResolvedValueOnce({ TimeZoneName: "America/Chicago" }); + const svc = freshService(); + expect(await svc.getMpTimezone()).toBe("America/Chicago"); + }); + + it("deduplicates concurrent first calls", async () => { + let resolveFn!: (v: { TimeZoneName: string }) => void; + mockGetDomainInfo.mockReturnValueOnce( + new Promise((res) => { resolveFn = res; }) + ); + const svc = freshService(); + const a = svc.getMpTimezone(); + const b = svc.getMpTimezone(); + resolveFn({ TimeZoneName: "Eastern Standard Time" }); + expect(await a).toBe("America/New_York"); + expect(await b).toBe("America/New_York"); + expect(mockGetDomainInfo).toHaveBeenCalledTimes(1); + }); + }); + + describe("toMpSqlDatetime", () => { + it("reformats a date-only string as MP-TZ midnight without conversion", async () => { + mockGetDomainInfo.mockResolvedValueOnce({ TimeZoneName: "Eastern Standard Time" }); + const svc = freshService(); + expect(await svc.toMpSqlDatetime("2026-05-17")).toBe("2026-05-17 00:00:00"); + }); + + it("preserves an already-SQL wall-clock value (no UTC math)", async () => { + mockGetDomainInfo.mockResolvedValueOnce({ TimeZoneName: "Eastern Standard Time" }); + const svc = freshService(); + expect(await svc.toMpSqlDatetime("2026-05-17 23:33:00")).toBe("2026-05-17 23:33:00"); + }); + + it("preserves a T-separated wall-clock value", async () => { + mockGetDomainInfo.mockResolvedValueOnce({ TimeZoneName: "Eastern Standard Time" }); + const svc = freshService(); + expect(await svc.toMpSqlDatetime("2026-05-17T14:30")).toBe("2026-05-17 14:30:00"); + }); + + it("converts a UTC-tagged instant into MP-TZ wall-clock", async () => { + mockGetDomainInfo.mockResolvedValueOnce({ TimeZoneName: "America/New_York" }); + const svc = freshService(); + expect(await svc.toMpSqlDatetime("2026-05-17T03:33:00.000Z")).toBe("2026-05-16 23:33:00"); + }); + + it("converts a Date instant into MP-TZ wall-clock", async () => { + mockGetDomainInfo.mockResolvedValueOnce({ TimeZoneName: "America/Los_Angeles" }); + const svc = freshService(); + const instant = new Date("2026-05-17T03:33:00.000Z"); + expect(await svc.toMpSqlDatetime(instant)).toBe("2026-05-16 20:33:00"); + }); + + it("regression: date-only input does NOT shift when server is in a different TZ", async () => { + mockGetDomainInfo.mockResolvedValueOnce({ TimeZoneName: "America/New_York" }); + const svc = freshService(); + expect(await svc.toMpSqlDatetime("2026-05-17")).toBe("2026-05-17 00:00:00"); + }); + + it("throws for unparseable input", async () => { + mockGetDomainInfo.mockResolvedValueOnce({ TimeZoneName: "Eastern Standard Time" }); + const svc = freshService(); + await expect(svc.toMpSqlDatetime("not a date")).rejects.toThrow(); + await expect(svc.toMpSqlDatetime("")).rejects.toThrow(); + }); + }); + + describe("parseMpDatetime", () => { + it("treats a wall-clock string as MP-TZ and returns the matching UTC instant", async () => { + mockGetDomainInfo.mockResolvedValueOnce({ TimeZoneName: "America/New_York" }); + const svc = freshService(); + const instant = await svc.parseMpDatetime("2026-05-17 12:00:00"); + expect(instant.toISOString()).toBe("2026-05-17T16:00:00.000Z"); + }); + + it("respects an explicit Z marker", async () => { + const svc = freshService(); + const instant = await svc.parseMpDatetime("2026-05-17T03:33:00.000Z"); + expect(instant.toISOString()).toBe("2026-05-17T03:33:00.000Z"); + }); + }); +}); +``` + +Run the service tests in isolation first: + +``` +npm run test:run -- src/services/domainTimezoneService.test.ts +``` + +Then re-run under two zones to prove the math is server-TZ independent: + +``` +TZ=UTC npm run test:run -- src/services/domainTimezoneService.test.ts +TZ=America/Los_Angeles npm run test:run -- src/services/domainTimezoneService.test.ts +``` + +All three runs must pass before moving on. + +## Phase 4 — Add the shared server action for client display + +Create a server action that exposes the MP timezone to client components. In the source repo this lives at `src/components/shared-actions/domain.ts`; in this repo, match the local convention from Phase 1. + +```ts +'use server'; + +import { DomainTimezoneService } from '@/services/domainTimezoneService'; + +/** + * Returns the IANA time zone identifier for the active Ministry Platform + * domain. Use this to drive any client-side `Intl.DateTimeFormat` rendering + * of MP-sourced datetime values so the displayed wall-clock matches MP's + * database regardless of the user's browser zone. + * + * Result is cached for the lifetime of the server process. + */ +export async function getMpTimezone(): Promise { + const tz = DomainTimezoneService.getInstance(); + return tz.getMpTimezone(); +} +``` + +## Phase 5 — Find and fix every MP datetime site + +For each anti-pattern site Phase 1 found, apply the appropriate recipe below. **Confirm the field is an MP datetime column** (not a local-only field, not a Better Auth field, not a UI-only filter) before changing anything. + +### Recipe A — form sends `${date}T00:00:00.000Z` + +**Symptom:** A form with `` (or a hidden field) appends a `Z` suffix or builds an ISO string before submission. + +**Fix:** Send the raw date string. The service handles the SQL formatting. + +```diff +- Contact_Date: `${data.contactDate}T00:00:00.000Z`, ++ Contact_Date: data.contactDate, +``` + +### Recipe B — service does `new Date(x).getFullYear()` round-trip + +**Symptom:** A service or server action receives a datetime string and reformats it using `new Date(x)` + `.getFullYear()` / `.getMonth()` / `.getDate()` / `.getHours()`. This reads in the **server's local zone**, which is silently wrong. + +**Fix:** Route the value through `DomainTimezoneService.toMpSqlDatetime()`. + +```diff ++ import { DomainTimezoneService } from "@/services/domainTimezoneService"; ++ + public async createContactLog(input: ContactLogInput): Promise { +- if (input.Contact_Date) { +- const date = new Date(input.Contact_Date); +- const year = date.getFullYear(); +- const month = String(date.getMonth() + 1).padStart(2, '0'); +- // ... etc, building "YYYY-MM-DD HH:MM:SS" +- input.Contact_Date = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; +- } ++ const tz = DomainTimezoneService.getInstance(); ++ const mpDate = await tz.toMpSqlDatetime(input.Contact_Date); + // ... pass mpDate through to MP + } +``` + +If the existing code validates a Zod-generated schema that declares `Contact_Date: z.string().datetime()` (ISO format), validate the **non-date** fields with the schema and re-attach the converted SQL string afterwards: + +```ts +const { Contact_Date, ...rest } = input; +const validatedRest = RecordSchema + .omit({ Some_PK_ID: true, Contact_Date: true }) + .parse(rest); +const tz = DomainTimezoneService.getInstance(); +const mpDate = await tz.toMpSqlDatetime(Contact_Date); +const payload = { ...validatedRest, Contact_Date: mpDate }; +``` + +### Recipe C — display uses `new Date(stringFromMp).toLocaleDateString(...)` + +**Symptom:** A client component renders an MP datetime via `new Date(...).toLocaleDateString()` or `.toLocaleString()`. This parses MP's wall-clock-in-MP-TZ as **browser-local**, then formats in the user's zone — silently wrong for any user not in MP-TZ. + +**Fix:** Receive the MP timezone (IANA) as a prop from a server component that called `getMpTimezone()`. Format with `Intl.DateTimeFormat({ timeZone })`. To convert MP's wall-clock string to the matching UTC instant for the formatter, build a candidate UTC and correct by the round-trip offset (same algorithm the service uses internally): + +```tsx +function formatMpDateTime(mpString: string, mpTimezone: string): string { + const normalized = mpString.replace("T", " ").split(".")[0]; + const match = normalized.match( + /^(\d{4})-(\d{2})-(\d{2})(?: (\d{2}):(\d{2})(?::(\d{2}))?)?(?:Z)?$/ + ); + let instant: Date; + if (match) { + const [, y, mo, d, h = "00", mi = "00", s = "00"] = match; + const utcGuess = Date.UTC(+y, +mo - 1, +d, +h, +mi, +s); + const parts = new Intl.DateTimeFormat("en-CA", { + timeZone: mpTimezone, + year: "numeric", month: "2-digit", day: "2-digit", + hour: "2-digit", minute: "2-digit", second: "2-digit", hour12: false, + }).formatToParts(new Date(utcGuess)); + const get = (t: string) => Number(parts.find((p) => p.type === t)!.value); + const projectedHour = get("hour") === 24 ? 0 : get("hour"); + const projectedUtc = Date.UTC( + get("year"), get("month") - 1, get("day"), + projectedHour, get("minute"), get("second") + ); + instant = new Date(utcGuess + (utcGuess - projectedUtc)); + } else { + instant = new Date(mpString); + } + return new Intl.DateTimeFormat("en-US", { + timeZone: mpTimezone, + month: "short", day: "numeric", year: "numeric", + hour: "numeric", minute: "2-digit", + }).format(instant); +} +``` + +Wire the timezone into the component tree: + +```tsx +// Server component (page or layout) +import { getMpTimezone } from "@/components/shared-actions/domain"; +const mpTimezone = await getMpTimezone(); +return ; +``` + +### Recipe D — edit form pre-fill via `new Date(...)` + +**Symptom:** An edit form reads an MP datetime and re-parses it: `setValue("date", new Date(log.Contact_Date).toISOString().split("T")[0])` or similar. + +**Fix:** Since MP returns wall-clock in MP-TZ already, take string slices directly. For a date input: + +```tsx +setValue("contactDate", log.Contact_Date.split("T")[0]); +``` + +For a `datetime-local` input (`YYYY-MM-DDTHH:MM`): + +```tsx +function toDatetimeLocalValue(mpDate: string): string { + const normalized = mpDate.replace(" ", "T"); + return normalized.length >= 16 ? normalized.slice(0, 16) : `${normalized.slice(0, 10)}T00:00`; +} +setValue("contactDate", toDatetimeLocalValue(log.Contact_Date)); +``` + +### Recipe E — `$filter` date literal built from `Date.toISOString()` + +**Symptom:** A filter string is built using `.toISOString()` or `.toUTCString()`. + +**Fix:** MP filters interpret literals in MP-TZ. Use `toMpSqlDatetime` to produce the right string: + +```ts +const tz = DomainTimezoneService.getInstance(); +const cutoff = await tz.toMpSqlDatetime(new Date()); +const filter = `Last_Activity_Date >= '${cutoff}'`; +``` + +### After every fix + +Update or add a test that asserts the produced string matches MP-TZ wall-clock. Run the suite under both `TZ=UTC` and `TZ=America/Los_Angeles`; the test must pass under both. + +## Phase 6 — Add a round-trip regression test for one real datetime field + +Pick whichever MP write path you fixed and add a test that proves the date no longer drifts. Pattern (from the contact-log fix in the source repo): + +```ts +it("regression: round-tripping the same edit does not shift the date", async () => { + mockUpdateTableRecords.mockResolvedValue([{ Contact_Log_ID: 1 }]); + + const service = await ContactLogService.getInstance(); + await service.updateContactLog(1, { Contact_Date: "2026-05-17" }); + await service.updateContactLog(1, { Contact_Date: "2026-05-17" }); + await service.updateContactLog(1, { Contact_Date: "2026-05-17" }); + + for (const call of mockUpdateTableRecords.mock.calls) { + expect(call[1][0].Contact_Date).toBe("2026-05-17 00:00:00"); + } +}); +``` + +If a service test file uses the singleton, reset both singletons in `beforeEach` and mock `getDomainInfo`: + +```ts +beforeEach(() => { + mockGetDomainInfo.mockReset(); + mockGetDomainInfo.mockResolvedValue({ TimeZoneName: "America/New_York" }); + (ContactLogService as any).instance = undefined; + (DomainTimezoneService as any).instance = null; +}); +``` + +## Phase 7 — Write the reference doc + +Create `.claude/references/ministryplatform.datetimehandling.md` with this content. Adjust import paths if this repo uses a different alias or directory layout. + +````markdown +# MP Date/Time Handling Reference + +This document covers how date and datetime values must flow between the UI, our services, and the Ministry Platform (MP) API. Use it whenever you add a new MP date field, audit a server action that writes dates, or debug a "the saved date is wrong" report. + +## Why MP is not UTC + +MP stores datetimes as **wall-clock values in the domain's configured time zone** (e.g. `2026-05-17 23:33:00` is literally "11:33 PM in this church's time zone"). It does **not** normalize to UTC on the way in or out. The domain's time zone is exposed via `MPHelper.getDomainInfo().TimeZoneName`. + +If you send a value tagged as UTC, MP stores it as if those UTC clock numbers were the local clock numbers — the saved record drifts by the MP-to-UTC offset. The same anti-pattern in reverse on the read path causes drift on display and compounds across edits. + +A real symptom of this bug: a Contact Log entry created at 11:33 PM Eastern on 2026-05-17 saved as 2026-05-16 at 8:00 PM. The form appended `T00:00:00.000Z` to a date string, and the service ran `new Date(...).getFullYear()` on the result. Each save shifted the date by the offset between the Node server's local time and UTC. Editing read the already-shifted date and applied the same transform again, so the date moved backwards another day every edit. + +## The service + +`src/services/domainTimezoneService.ts` — singleton, server-side, cached per process. Always go through this; never reach into `MPHelper.getDomainInfo()` directly to read `TimeZoneName`. + +```ts +import { DomainTimezoneService } from "@/services/domainTimezoneService"; + +const tz = DomainTimezoneService.getInstance(); +await tz.getMpTimezone(); // → "America/New_York" (IANA) +await tz.toMpSqlDatetime("2026-05-17"); // → "2026-05-17 00:00:00" +await tz.toMpSqlDatetime(new Date()); // → MP-TZ wall-clock for "now" +await tz.parseMpDatetime("2026-05-17 12:00:00"); // → Date instant +``` + +For client-side rendering, expose the IANA zone through `getMpTimezone()` in `src/components/shared-actions/domain.ts` and thread it as a prop into the component that needs to format MP datetimes. + +### `toMpSqlDatetime(value)` — write path + +Returns the SQL datetime string MP's table API expects (`YYYY-MM-DD HH:MM:SS`). + +| Input | Treated as | Output | +| --- | --- | --- | +| `"2026-05-17"` | MP-TZ wall-clock midnight | `"2026-05-17 00:00:00"` | +| `"2026-05-17 14:30:00"` | MP-TZ wall-clock (already SQL) | `"2026-05-17 14:30:00"` | +| `"2026-05-17T14:30"` | MP-TZ wall-clock | `"2026-05-17 14:30:00"` | +| `"2026-05-17T03:33:00.000Z"` | UTC instant | converted to MP-TZ | +| `"2026-05-17T03:33:00-04:00"` | Instant at offset | converted to MP-TZ | +| `Date` instance | UTC instant | converted to MP-TZ | + +The rule: **strings with no zone marker are wall-clock**, strings/Dates with explicit zone info are instants that get converted. + +### `parseMpDatetime(value)` — read path arithmetic + +Use when you need a `Date` instant to do real arithmetic on a value MP returned. For pure display, prefer `Intl.DateTimeFormat({ timeZone })` against the raw string. + +## Recipes + +### Writing a date-only field (``) + +```tsx +// Client component — send the raw string, no Z, no time. +const payload = { Contact_Date: form.contactDate /* "2026-05-17" */ }; + +// Server action / service +const tz = DomainTimezoneService.getInstance(); +const mpDate = await tz.toMpSqlDatetime(payload.Contact_Date); +// → "2026-05-17 00:00:00" +``` + +### Writing a datetime field with a "save at current moment" intent + +```ts +const tz = DomainTimezoneService.getInstance(); +const mpDate = await tz.toMpSqlDatetime(new Date()); +// → MP-TZ wall-clock representation of the server's "now" +``` + +### Pre-filling an edit form from a stored MP value + +MP returns datetimes as wall-clock strings in MP-TZ (no zone marker). For a date input, take the date portion directly — **do not** parse with `new Date()`: + +```tsx +setValue("contactDate", log.Contact_Date.split("T")[0]); +``` + +For a `datetime-local` input, trim to `YYYY-MM-DDTHH:MM`: + +```tsx +function toDatetimeLocalValue(mpDate: string): string { + const normalized = mpDate.replace(" ", "T"); + return normalized.length >= 16 ? normalized.slice(0, 16) : `${normalized.slice(0, 10)}T00:00`; +} +``` + +### Displaying a stored MP datetime in the browser + +`new Date(stringFromMp).toLocaleDateString(...)` parses the string as **browser-local**, which silently disagrees with MP-TZ. Format with an explicit `timeZone`: + +```tsx +return new Intl.DateTimeFormat("en-US", { + timeZone: mpTimezone, + month: "short", day: "numeric", year: "numeric", + hour: "numeric", minute: "2-digit", +}).format(instant); +``` + +### Filtering on a date column in `$filter` + +`$filter` strings are interpreted in MP-TZ. Quote the value and use MP-TZ wall-clock: + +```ts +filter: `Contact_Date >= '2026-05-01' AND Contact_Date < '2026-06-01'` +``` + +Do not convert filter values to UTC. If you have a `Date` instant in JS, run it through `tz.toMpSqlDatetime(instant)` first. + +## Anti-patterns + +| ❌ Don't | ✅ Do | +| --- | --- | +| ``Contact_Date: `${date}T00:00:00.000Z` `` | `Contact_Date: date` | +| `new Date(formValue).toISOString()` | `await tz.toMpSqlDatetime(formValue)` | +| `new Date(mpValue).getFullYear()` etc. | `await tz.parseMpDatetime(mpValue)` or `Intl.DateTimeFormat({ timeZone })` | +| `new Date(mpValue).toLocaleString(...)` for display | `Intl.DateTimeFormat("en-US", { timeZone: mpTimezone, ... })` | +| Reading domain TZ ad-hoc per request | `DomainTimezoneService.getInstance().getMpTimezone()` (cached) | + +The shared signature of these bugs: a `Date` object that crosses a zone boundary silently. Whenever you see `new Date(...)` near an MP read/write, ask "what zone is this assumed to be in, and what zone is the caller expecting back?" + +## Windows ↔ IANA zone names + +MP's `/domain` endpoint returns `TimeZoneName` as a **Windows** zone (e.g. `"Eastern Standard Time"`). `Intl.DateTimeFormat` requires **IANA** (e.g. `"America/New_York"`). `DomainTimezoneService` maps between them. If a new MP deployment surfaces an unmapped zone, `resolveIanaTimezone` throws with the unmapped name — extend the table rather than silently falling back to the server's local zone. + +## Testing + +When a test exercises code that goes through `DomainTimezoneService`: + +1. **Mock `MPHelper.getDomainInfo`** to return a known `TimeZoneName` — use `vi.hoisted()` (Vitest) because the singleton's `MPHelper` is constructed at module-load time. +2. **Reset the singleton** between tests: `(DomainTimezoneService as any).instance = null` in `beforeEach`. +3. **Use `mockReset()` (not `clearAllMocks()`)** on the `getDomainInfo` mock. `clearAllMocks` doesn't drain `mockResolvedValueOnce` queues, and tests that don't hit `getMpTimezone()` leave queue entries behind that leak forward. +4. **Run under multiple `TZ` env vars** — at minimum `TZ=UTC` and `TZ=America/Los_Angeles`. The original bug was invisible when developer machines and the server happened to be in the same zone as the MP domain. +```` + +## Phase 8 — Update CLAUDE.md + +Two edits to the repo's `CLAUDE.md`: + +**1.** Add a new bullet to the **Key Development Practices** section. Number it to follow the existing list (in the source repo this was #10; in this repo it may be different — match the local convention): + +``` +N. **Convert all date/time values at the MP boundary** - use `DomainTimezoneService` (never raw `new Date(x).toISOString()` or `getFullYear()`) when sending or receiving datetime fields, since MP stores wall-clock values in the domain's time zone, not UTC. See **[Date/Time Handling Reference](.claude/references/ministryplatform.datetimehandling.md)**. +``` + +**2.** Add a line to the **Reference Documents** section pointing to the new doc: + +``` +- **[Ministry Platform Date/Time Handling](.claude/references/ministryplatform.datetimehandling.md)** - How to send/receive MP datetimes safely via `DomainTimezoneService`, anti-patterns, Windows↔IANA mapping, and test guidance +``` + +If this repo's CLAUDE.md doesn't have a "Reference Documents" section yet, create it after Key Development Practices. + +## Phase 9 — Verify + +Before declaring done: + +1. `npm run lint` — clean. +2. `npm run test:run` (or whatever the local test command is) — all tests pass. +3. `npx tsc --noEmit` — no new type errors. Pre-existing errors in unrelated files are OK; note them in the PR description. +4. Run the service tests under `TZ=UTC` and `TZ=America/Los_Angeles` and confirm both pass: + ``` + TZ=UTC npm run test:run -- src/services/domainTimezoneService.test.ts + TZ=America/Los_Angeles npm run test:run -- src/services/domainTimezoneService.test.ts + ``` +5. Spot-check one bug fix manually if possible: open the app, exercise a date-handling feature, confirm the saved value matches what was entered and that editing without changing fields doesn't shift the value. + +## Phase 10 — Branch, commit, PR + +Use the repo's existing conventions. The source repo's commit message looked like this; adapt the scope to whichever feature carried the bug in this repo: + +``` +fix(): correct timezone handling on save/edit + +MP stores datetimes as wall-clock values in the domain's configured time +zone, not UTC. tagged values as UTC and round-tripped them +through `new Date(...).getFullYear()`, producing strings in the Node +server's local zone instead of MP's. Edits compounded the drift. + +- Add DomainTimezoneService — singleton wrapping getDomainInfo() with + Windows→IANA mapping and SQL datetime conversion. +- Add shared server action `getMpTimezone()` for client-side display. +- Fix to route MP date columns through the service. +- Add reference doc `.claude/references/ministryplatform.datetimehandling.md`. +- Update CLAUDE.md with Key Development Practice + reference link. + +Tests: new, , suite passes under +TZ=UTC and TZ=America/Los_Angeles. +``` + +Open the PR, request review, do not self-merge unless that's normal in this repo. + +## What "done" looks like + +- [ ] `src/services/domainTimezoneService.ts` exists and is the only file calling `MPHelper.getDomainInfo()` for `TimeZoneName`. +- [ ] `src/services/domainTimezoneService.test.ts` exists with at least the 16 tests above; passes under `TZ=UTC` and `TZ=America/Los_Angeles`. +- [ ] No remaining hits in `src/` for these greps on MP-bound date columns: + - `T00:00:00.000Z` + - `.toISOString()` *near* an MP field + - `new Date(.*).getFullYear()` *near* an MP read/write + - `new Date(.*).toLocaleDateString` *for an MP-sourced value* +- [ ] At least one feature has a round-trip regression test asserting no drift across three save cycles. +- [ ] `.claude/references/ministryplatform.datetimehandling.md` exists. +- [ ] `CLAUDE.md` has a Key Development Practices bullet and a Reference Documents entry pointing to it. +- [ ] Lint and full test suite pass. + +If you hit something the playbook doesn't cover — a different MPHelper shape, an unusual existing partial fix, a non-MP date that grep flagged — stop and ask the user before improvising. diff --git a/.claude/references/ministryplatform.datetimehandling.md b/.claude/references/ministryplatform.datetimehandling.md new file mode 100644 index 0000000..87b5abc --- /dev/null +++ b/.claude/references/ministryplatform.datetimehandling.md @@ -0,0 +1,134 @@ +# MP Date/Time Handling Reference + +This document covers how date and datetime values must flow between the UI, our services, and the Ministry Platform (MP) API. Use it whenever you add a new MP date field, audit a server action that writes dates, or debug a "the saved date is wrong" report. + +## Why MP is not UTC + +MP stores datetimes as **wall-clock values in the domain's configured time zone** (e.g. `2026-05-17 23:33:00` is literally "11:33 PM in this church's time zone"). It does **not** normalize to UTC on the way in or out. The domain's time zone is exposed via `MPHelper.getDomainInfo().TimeZoneName`. + +If you send a value tagged as UTC, MP stores it as if those UTC clock numbers were the local clock numbers — the saved record drifts by the MP-to-UTC offset. The same anti-pattern in reverse on the read path causes drift on display and compounds across edits. + +A real symptom of this bug: a Contact Log entry created at 11:33 PM Eastern on 2026-05-17 saved as 2026-05-16 at 8:00 PM. The form appended `T00:00:00.000Z` to a date string, and the service ran `new Date(...).getFullYear()` on the result. Each save shifted the date by the offset between the Node server's local time and UTC. Editing read the already-shifted date and applied the same transform again, so the date moved backwards another day every edit. + +## The service + +`src/services/domainTimezoneService.ts` — singleton, server-side, cached per process. Always go through this; never reach into `MPHelper.getDomainInfo()` directly to read `TimeZoneName`. + +```ts +import { DomainTimezoneService } from "@/services/domainTimezoneService"; + +const tz = DomainTimezoneService.getInstance(); +await tz.getMpTimezone(); // → "America/New_York" (IANA) +await tz.toMpSqlDatetime("2026-05-17"); // → "2026-05-17 00:00:00" +await tz.toMpSqlDatetime(new Date()); // → MP-TZ wall-clock for "now" +await tz.parseMpDatetime("2026-05-17 12:00:00"); // → Date instant +``` + +For client-side rendering, expose the IANA zone through `getMpTimezone()` in `src/app/actions/domain.ts` and thread it as a prop into the component that needs to format MP datetimes. + +### `toMpSqlDatetime(value)` — write path + +Returns the SQL datetime string MP's table API expects (`YYYY-MM-DD HH:MM:SS`). + +| Input | Treated as | Output | +| --- | --- | --- | +| `"2026-05-17"` | MP-TZ wall-clock midnight | `"2026-05-17 00:00:00"` | +| `"2026-05-17 14:30:00"` | MP-TZ wall-clock (already SQL) | `"2026-05-17 14:30:00"` | +| `"2026-05-17T14:30"` | MP-TZ wall-clock | `"2026-05-17 14:30:00"` | +| `"2026-05-17T03:33:00.000Z"` | UTC instant | converted to MP-TZ | +| `"2026-05-17T03:33:00-04:00"` | Instant at offset | converted to MP-TZ | +| `Date` instance | UTC instant | converted to MP-TZ | + +The rule: **strings with no zone marker are wall-clock**, strings/Dates with explicit zone info are instants that get converted. + +### `parseMpDatetime(value)` — read path arithmetic + +Use when you need a `Date` instant to do real arithmetic on a value MP returned. For pure display, prefer `Intl.DateTimeFormat({ timeZone })` against the raw string. + +## Recipes + +### Writing a date-only field (``) + +```tsx +// Client component — send the raw string, no Z, no time. +const payload = { Contact_Date: form.contactDate /* "2026-05-17" */ }; + +// Server action / service +const tz = DomainTimezoneService.getInstance(); +const mpDate = await tz.toMpSqlDatetime(payload.Contact_Date); +// → "2026-05-17 00:00:00" +``` + +### Writing a datetime field with a "save at current moment" intent + +```ts +const tz = DomainTimezoneService.getInstance(); +const mpDate = await tz.toMpSqlDatetime(new Date()); +// → MP-TZ wall-clock representation of the server's "now" +``` + +### Pre-filling an edit form from a stored MP value + +MP returns datetimes as wall-clock strings in MP-TZ (no zone marker). For a date input, take the date portion directly — **do not** parse with `new Date()`: + +```tsx +setValue("contactDate", log.Contact_Date.split("T")[0]); +``` + +For a `datetime-local` input, trim to `YYYY-MM-DDTHH:MM`: + +```tsx +function toDatetimeLocalValue(mpDate: string): string { + const normalized = mpDate.replace(" ", "T"); + return normalized.length >= 16 ? normalized.slice(0, 16) : `${normalized.slice(0, 10)}T00:00`; +} +``` + +### Displaying a stored MP datetime in the browser + +`new Date(stringFromMp).toLocaleDateString(...)` parses the string as **browser-local**, which silently disagrees with MP-TZ. Format with an explicit `timeZone`: + +```tsx +return new Intl.DateTimeFormat("en-US", { + timeZone: mpTimezone, + month: "short", day: "numeric", year: "numeric", + hour: "numeric", minute: "2-digit", +}).format(instant); +``` + +For embed-SDK Web Components specifically, the IANA zone must be fetched (or returned in the API payload) and passed into the component — see `src/services/fullCalendarService.ts` for an example of routing the value through `DomainTimezoneService.toMpSqlDatetime` on the server side before MP `$filter` is composed. + +### Filtering on a date column in `$filter` + +`$filter` strings are interpreted in MP-TZ. Quote the value and use MP-TZ wall-clock: + +```ts +filter: `Contact_Date >= '2026-05-01' AND Contact_Date < '2026-06-01'` +``` + +Do not convert filter values to UTC. If you have a `Date` instant or an ISO/Z-tagged string in JS, run it through `tz.toMpSqlDatetime(instant)` first. + +## Anti-patterns + +| ❌ Don't | ✅ Do | +| --- | --- | +| ``Contact_Date: `${date}T00:00:00.000Z` `` | `Contact_Date: date` | +| `new Date(formValue).toISOString()` | `await tz.toMpSqlDatetime(formValue)` | +| `new Date(mpValue).getFullYear()` etc. | `await tz.parseMpDatetime(mpValue)` or `Intl.DateTimeFormat({ timeZone })` | +| `new Date(mpValue).toLocaleString(...)` for display | `Intl.DateTimeFormat("en-US", { timeZone: mpTimezone, ... })` | +| Reading domain TZ ad-hoc per request | `DomainTimezoneService.getInstance().getMpTimezone()` (cached) | + +The shared signature of these bugs: a `Date` object that crosses a zone boundary silently. Whenever you see `new Date(...)` near an MP read/write, ask "what zone is this assumed to be in, and what zone is the caller expecting back?" + +## Windows ↔ IANA zone names + +MP's `/domain` endpoint returns `TimeZoneName` as a **Windows** zone (e.g. `"Eastern Standard Time"`). `Intl.DateTimeFormat` requires **IANA** (e.g. `"America/New_York"`). `DomainTimezoneService` maps between them. If a new MP deployment surfaces an unmapped zone, `resolveIanaTimezone` throws with the unmapped name — extend the table rather than silently falling back to the server's local zone. + +## Testing + +When a test exercises code that goes through `DomainTimezoneService`: + +1. **Mock `MPHelper.getDomainInfo`** to return a known `TimeZoneName` — use `vi.hoisted()` because the singleton's `MPHelper` is constructed at module-load time. +2. **Reset the singleton** between tests: `(DomainTimezoneService as any).instance = null` in `beforeEach`. +3. **Use `mockReset()` (not `clearAllMocks()`)** on the `getDomainInfo` mock. `clearAllMocks` doesn't drain `mockResolvedValueOnce` queues, and tests that don't hit `getMpTimezone()` leave queue entries behind that leak forward. +4. **Run under multiple `TZ` env vars** — at minimum `TZ=UTC` and `TZ=America/Los_Angeles`. The original bug was invisible when developer machines and the server happened to be in the same zone as the MP domain. diff --git a/CLAUDE.md b/CLAUDE.md index 5cdc764..0b5a5b8 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -62,7 +62,13 @@ Manual widget testing via `pnpm test:widget` (opens http://localhost:5173). Play All services follow singleton pattern: `const svc = await ServiceName.getInstance()`. Each wraps `MPHelper`. -Services: `addToCalendar`, `fullCalendar`, `profile`, `subscription`, `user` +Services: `addToCalendar`, `fullCalendar`, `profile`, `subscription`, `user`, `domainTimezone` + +## MP Date/Time Handling + +**Convert all date/time values at the MP boundary** — use `DomainTimezoneService` (never raw `new Date(x).toISOString()` or `getFullYear()`) when sending or receiving datetime fields, since MP stores wall-clock values in the domain's time zone, not UTC. Server-side, route writes/filters through `DomainTimezoneService.getInstance().toMpSqlDatetime(...)`. Client-side, format MP values with `Intl.DateTimeFormat({ timeZone })` using the IANA zone from `getMpTimezone()` (`src/app/actions/domain.ts`). + +See **[Date/Time Handling Reference](.claude/references/ministryplatform.datetimehandling.md)**. ## Code Conventions @@ -114,3 +120,6 @@ await mp.executeProcedure('ProcName', { param: 'value' }); | `packages/embed-sdk/vite.config.ts` | Vite library mode (ES + UMD output) | | `public/embed-sdk/mp-widget-overrides.css` | Brand CSS for MP Shadow DOM widgets | | `.claude/references/ministryplatform.query-syntax.md` | MP REST API query syntax reference (`$filter`, `$select`, `_TABLE` traversal) | +| `.claude/references/ministryplatform.datetimehandling.md` | How to send/receive MP datetimes safely via `DomainTimezoneService`, anti-patterns, Windows↔IANA mapping, test guidance | +| `src/services/domainTimezoneService.ts` | Singleton: MP domain TZ → IANA, `toMpSqlDatetime`, `parseMpDatetime` | +| `src/app/actions/domain.ts` | `getMpTimezone()` server action for client-side `Intl.DateTimeFormat` rendering | diff --git a/next.config.ts b/next.config.ts index e9ffa30..8dbc168 100644 --- a/next.config.ts +++ b/next.config.ts @@ -1,7 +1,19 @@ import type { NextConfig } from "next"; +import { readFileSync } from "node:fs"; +import { join } from "node:path"; + +function readVersion(): string { + try { + return readFileSync(join(process.cwd(), "VERSION"), "utf8").trim(); + } catch { + return "dev"; + } +} const nextConfig: NextConfig = { - /* config options here */ + env: { + NEXT_PUBLIC_APP_VERSION: readVersion(), + }, }; export default nextConfig; diff --git a/src/app/actions/domain.ts b/src/app/actions/domain.ts new file mode 100644 index 0000000..a5b9ea8 --- /dev/null +++ b/src/app/actions/domain.ts @@ -0,0 +1,16 @@ +"use server"; + +import { DomainTimezoneService } from "@/services/domainTimezoneService"; + +/** + * Returns the IANA time zone identifier for the active Ministry Platform + * domain. Use this to drive any client-side `Intl.DateTimeFormat` rendering + * of MP-sourced datetime values so the displayed wall-clock matches MP's + * database regardless of the user's browser zone. + * + * Result is cached for the lifetime of the server process. + */ +export async function getMpTimezone(): Promise { + const tz = DomainTimezoneService.getInstance(); + return tz.getMpTimezone(); +} diff --git a/src/lib/providers/ministry-platform/docs/README.md b/src/lib/providers/ministry-platform/docs/README.md index 0e8fb21..9fc31ba 100644 --- a/src/lib/providers/ministry-platform/docs/README.md +++ b/src/lib/providers/ministry-platform/docs/README.md @@ -65,9 +65,14 @@ const contacts = await mp.getTableRecords({ }); // Create contact log +// For MP datetime columns, route through DomainTimezoneService — MP stores +// wall-clock in the domain's time zone, not UTC. +// See .claude/references/ministryplatform.datetimehandling.md +import { DomainTimezoneService } from '@/services/domainTimezoneService'; +const tz = DomainTimezoneService.getInstance(); await mp.createTableRecords('Contact_Log', [{ Contact_ID: 12345, - Contact_Date: new Date().toISOString(), + Contact_Date: await tz.toMpSqlDatetime(new Date()), Made_By: 1, Notes: 'Follow-up call completed' }]); diff --git a/src/lib/providers/ministry-platform/helper.ts b/src/lib/providers/ministry-platform/helper.ts index 6878dba..cc25577 100644 --- a/src/lib/providers/ministry-platform/helper.ts +++ b/src/lib/providers/ministry-platform/helper.ts @@ -156,9 +156,13 @@ export class MPHelper { * // Create with Zod validation (recommended) * import { ContactLogSchema } from '@/lib/providers/ministry-platform/models'; * + * // For MP datetime columns, route the value through DomainTimezoneService + * // — MP stores wall-clock in the domain's time zone, not UTC. + * import { DomainTimezoneService } from '@/services/domainTimezoneService'; + * const tz = DomainTimezoneService.getInstance(); * const contactLogs = await mp.createTableRecords('Contact_Log', [{ * Contact_ID: 12345, - * Contact_Date: new Date().toISOString(), + * Contact_Date: await tz.toMpSqlDatetime(new Date()), * Made_By: 1, * Notes: 'Follow-up call completed' * }], { diff --git a/src/services/domainTimezoneService.test.ts b/src/services/domainTimezoneService.test.ts new file mode 100644 index 0000000..73fa8ba --- /dev/null +++ b/src/services/domainTimezoneService.test.ts @@ -0,0 +1,154 @@ +import { describe, it, expect, vi, beforeEach } from "vitest"; + +const { mockGetDomainInfo } = vi.hoisted(() => ({ + mockGetDomainInfo: vi.fn(), +})); + +vi.mock("@/lib/providers/ministry-platform", () => { + return { + MPHelper: class { + getDomainInfo = mockGetDomainInfo; + }, + }; +}); + +import { + DomainTimezoneService, + resolveIanaTimezone, +} from "@/services/domainTimezoneService"; + +function freshService(): DomainTimezoneService { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (DomainTimezoneService as any).instance = null; + return DomainTimezoneService.getInstance(); +} + +describe("resolveIanaTimezone", () => { + it("maps common Windows zone names to IANA", () => { + expect(resolveIanaTimezone("Eastern Standard Time")).toBe("America/New_York"); + expect(resolveIanaTimezone("Central Standard Time")).toBe("America/Chicago"); + expect(resolveIanaTimezone("Pacific Standard Time")).toBe("America/Los_Angeles"); + expect(resolveIanaTimezone("GMT Standard Time")).toBe("Europe/London"); + }); + + it("passes through IANA zone names unchanged", () => { + expect(resolveIanaTimezone("America/Chicago")).toBe("America/Chicago"); + expect(resolveIanaTimezone("Europe/Berlin")).toBe("Europe/Berlin"); + }); + + it("normalizes UTC variants", () => { + expect(resolveIanaTimezone("UTC")).toBe("Etc/UTC"); + expect(resolveIanaTimezone("Etc/UTC")).toBe("Etc/UTC"); + }); + + it("throws for unknown identifiers rather than silently falling back", () => { + expect(() => resolveIanaTimezone("Atlantis Standard Time")).toThrow(/Unknown time zone/); + expect(() => resolveIanaTimezone("")).toThrow(); + }); +}); + +describe("DomainTimezoneService", () => { + beforeEach(() => { + // mockReset (not clearAllMocks) so mockResolvedValueOnce queues are drained + // between tests — date-only paths skip getMpTimezone() and would otherwise + // leak unconsumed queue entries forward. + mockGetDomainInfo.mockReset(); + }); + + describe("getMpTimezone", () => { + it("fetches and caches the IANA zone after first call", async () => { + mockGetDomainInfo.mockResolvedValueOnce({ + TimeZoneName: "Eastern Standard Time", + DisplayName: "Test", + CultureName: "en-US", + }); + const svc = freshService(); + expect(await svc.getMpTimezone()).toBe("America/New_York"); + expect(await svc.getMpTimezone()).toBe("America/New_York"); + expect(mockGetDomainInfo).toHaveBeenCalledTimes(1); + }); + + it("accepts an IANA zone from MP without mapping", async () => { + mockGetDomainInfo.mockResolvedValueOnce({ TimeZoneName: "America/Chicago" }); + const svc = freshService(); + expect(await svc.getMpTimezone()).toBe("America/Chicago"); + }); + + it("deduplicates concurrent first calls", async () => { + let resolveFn!: (v: { TimeZoneName: string }) => void; + mockGetDomainInfo.mockReturnValueOnce( + new Promise((res) => { + resolveFn = res; + }) + ); + const svc = freshService(); + const a = svc.getMpTimezone(); + const b = svc.getMpTimezone(); + resolveFn({ TimeZoneName: "Eastern Standard Time" }); + expect(await a).toBe("America/New_York"); + expect(await b).toBe("America/New_York"); + expect(mockGetDomainInfo).toHaveBeenCalledTimes(1); + }); + }); + + describe("toMpSqlDatetime", () => { + it("reformats a date-only string as MP-TZ midnight without conversion", async () => { + mockGetDomainInfo.mockResolvedValueOnce({ TimeZoneName: "Eastern Standard Time" }); + const svc = freshService(); + expect(await svc.toMpSqlDatetime("2026-05-17")).toBe("2026-05-17 00:00:00"); + }); + + it("preserves an already-SQL wall-clock value (no UTC math)", async () => { + mockGetDomainInfo.mockResolvedValueOnce({ TimeZoneName: "Eastern Standard Time" }); + const svc = freshService(); + expect(await svc.toMpSqlDatetime("2026-05-17 23:33:00")).toBe("2026-05-17 23:33:00"); + }); + + it("preserves a T-separated wall-clock value", async () => { + mockGetDomainInfo.mockResolvedValueOnce({ TimeZoneName: "Eastern Standard Time" }); + const svc = freshService(); + expect(await svc.toMpSqlDatetime("2026-05-17T14:30")).toBe("2026-05-17 14:30:00"); + }); + + it("converts a UTC-tagged instant into MP-TZ wall-clock", async () => { + mockGetDomainInfo.mockResolvedValueOnce({ TimeZoneName: "America/New_York" }); + const svc = freshService(); + expect(await svc.toMpSqlDatetime("2026-05-17T03:33:00.000Z")).toBe("2026-05-16 23:33:00"); + }); + + it("converts a Date instant into MP-TZ wall-clock", async () => { + mockGetDomainInfo.mockResolvedValueOnce({ TimeZoneName: "America/Los_Angeles" }); + const svc = freshService(); + const instant = new Date("2026-05-17T03:33:00.000Z"); + expect(await svc.toMpSqlDatetime(instant)).toBe("2026-05-16 20:33:00"); + }); + + it("regression: date-only input does NOT shift when server is in a different TZ", async () => { + mockGetDomainInfo.mockResolvedValueOnce({ TimeZoneName: "America/New_York" }); + const svc = freshService(); + expect(await svc.toMpSqlDatetime("2026-05-17")).toBe("2026-05-17 00:00:00"); + }); + + it("throws for unparseable input", async () => { + mockGetDomainInfo.mockResolvedValueOnce({ TimeZoneName: "Eastern Standard Time" }); + const svc = freshService(); + await expect(svc.toMpSqlDatetime("not a date")).rejects.toThrow(); + await expect(svc.toMpSqlDatetime("")).rejects.toThrow(); + }); + }); + + describe("parseMpDatetime", () => { + it("treats a wall-clock string as MP-TZ and returns the matching UTC instant", async () => { + mockGetDomainInfo.mockResolvedValueOnce({ TimeZoneName: "America/New_York" }); + const svc = freshService(); + const instant = await svc.parseMpDatetime("2026-05-17 12:00:00"); + expect(instant.toISOString()).toBe("2026-05-17T16:00:00.000Z"); + }); + + it("respects an explicit Z marker", async () => { + const svc = freshService(); + const instant = await svc.parseMpDatetime("2026-05-17T03:33:00.000Z"); + expect(instant.toISOString()).toBe("2026-05-17T03:33:00.000Z"); + }); + }); +}); diff --git a/src/services/domainTimezoneService.ts b/src/services/domainTimezoneService.ts new file mode 100644 index 0000000..96c167b --- /dev/null +++ b/src/services/domainTimezoneService.ts @@ -0,0 +1,344 @@ +import { MPHelper } from "@/lib/providers/ministry-platform"; + +/** + * Mapping of common Windows time zone IDs (as returned by the MP /domain endpoint's + * `TimeZoneName` field) to IANA time zone identifiers (which `Intl.DateTimeFormat` + * requires). Extend as new MP-hosted domains surface zones not listed here. + */ +const WINDOWS_TO_IANA: Record = { + "Dateline Standard Time": "Etc/GMT+12", + "UTC-11": "Etc/GMT+11", + "Aleutian Standard Time": "America/Adak", + "Hawaiian Standard Time": "Pacific/Honolulu", + "Marquesas Standard Time": "Pacific/Marquesas", + "Alaskan Standard Time": "America/Anchorage", + "UTC-09": "Etc/GMT+9", + "Pacific Standard Time (Mexico)": "America/Tijuana", + "UTC-08": "Etc/GMT+8", + "Pacific Standard Time": "America/Los_Angeles", + "US Mountain Standard Time": "America/Phoenix", + "Mountain Standard Time (Mexico)": "America/Mazatlan", + "Mountain Standard Time": "America/Denver", + "Central America Standard Time": "America/Guatemala", + "Central Standard Time": "America/Chicago", + "Easter Island Standard Time": "Pacific/Easter", + "Central Standard Time (Mexico)": "America/Mexico_City", + "Canada Central Standard Time": "America/Regina", + "SA Pacific Standard Time": "America/Bogota", + "Eastern Standard Time (Mexico)": "America/Cancun", + "Eastern Standard Time": "America/New_York", + "Haiti Standard Time": "America/Port-au-Prince", + "Cuba Standard Time": "America/Havana", + "US Eastern Standard Time": "America/Indianapolis", + "Turks And Caicos Standard Time": "America/Grand_Turk", + "Paraguay Standard Time": "America/Asuncion", + "Atlantic Standard Time": "America/Halifax", + "Venezuela Standard Time": "America/Caracas", + "Central Brazilian Standard Time": "America/Cuiaba", + "SA Western Standard Time": "America/La_Paz", + "Pacific SA Standard Time": "America/Santiago", + "Newfoundland Standard Time": "America/St_Johns", + "Tocantins Standard Time": "America/Araguaina", + "E. South America Standard Time": "America/Sao_Paulo", + "SA Eastern Standard Time": "America/Cayenne", + "Argentina Standard Time": "America/Buenos_Aires", + "Greenland Standard Time": "America/Godthab", + "Montevideo Standard Time": "America/Montevideo", + "Magallanes Standard Time": "America/Punta_Arenas", + "Saint Pierre Standard Time": "America/Miquelon", + "Bahia Standard Time": "America/Bahia", + "UTC-02": "Etc/GMT+2", + "Azores Standard Time": "Atlantic/Azores", + "Cape Verde Standard Time": "Atlantic/Cape_Verde", + UTC: "Etc/UTC", + "GMT Standard Time": "Europe/London", + "Greenwich Standard Time": "Atlantic/Reykjavik", + "Sao Tome Standard Time": "Africa/Sao_Tome", + "Morocco Standard Time": "Africa/Casablanca", + "W. Europe Standard Time": "Europe/Berlin", + "Central Europe Standard Time": "Europe/Budapest", + "Romance Standard Time": "Europe/Paris", + "Central European Standard Time": "Europe/Warsaw", + "W. Central Africa Standard Time": "Africa/Lagos", + "Jordan Standard Time": "Asia/Amman", + "GTB Standard Time": "Europe/Bucharest", + "Middle East Standard Time": "Asia/Beirut", + "Egypt Standard Time": "Africa/Cairo", + "E. Europe Standard Time": "Europe/Chisinau", + "Syria Standard Time": "Asia/Damascus", + "West Bank Standard Time": "Asia/Hebron", + "South Africa Standard Time": "Africa/Johannesburg", + "FLE Standard Time": "Europe/Kiev", + "Israel Standard Time": "Asia/Jerusalem", + "Kaliningrad Standard Time": "Europe/Kaliningrad", + "Sudan Standard Time": "Africa/Khartoum", + "Libya Standard Time": "Africa/Tripoli", + "Namibia Standard Time": "Africa/Windhoek", + "Arabic Standard Time": "Asia/Baghdad", + "Turkey Standard Time": "Europe/Istanbul", + "Arab Standard Time": "Asia/Riyadh", + "Belarus Standard Time": "Europe/Minsk", + "Russian Standard Time": "Europe/Moscow", + "E. Africa Standard Time": "Africa/Nairobi", + "Iran Standard Time": "Asia/Tehran", + "Arabian Standard Time": "Asia/Dubai", + "Astrakhan Standard Time": "Europe/Astrakhan", + "Azerbaijan Standard Time": "Asia/Baku", + "Russia Time Zone 3": "Europe/Samara", + "Mauritius Standard Time": "Indian/Mauritius", + "Saratov Standard Time": "Europe/Saratov", + "Georgian Standard Time": "Asia/Tbilisi", + "Volgograd Standard Time": "Europe/Volgograd", + "Caucasus Standard Time": "Asia/Yerevan", + "Afghanistan Standard Time": "Asia/Kabul", + "West Asia Standard Time": "Asia/Tashkent", + "Ekaterinburg Standard Time": "Asia/Yekaterinburg", + "Pakistan Standard Time": "Asia/Karachi", + "Qyzylorda Standard Time": "Asia/Qyzylorda", + "India Standard Time": "Asia/Calcutta", + "Sri Lanka Standard Time": "Asia/Colombo", + "Nepal Standard Time": "Asia/Katmandu", + "Central Asia Standard Time": "Asia/Almaty", + "Bangladesh Standard Time": "Asia/Dhaka", + "Omsk Standard Time": "Asia/Omsk", + "Myanmar Standard Time": "Asia/Rangoon", + "SE Asia Standard Time": "Asia/Bangkok", + "Altai Standard Time": "Asia/Barnaul", + "W. Mongolia Standard Time": "Asia/Hovd", + "North Asia Standard Time": "Asia/Krasnoyarsk", + "N. Central Asia Standard Time": "Asia/Novosibirsk", + "Tomsk Standard Time": "Asia/Tomsk", + "China Standard Time": "Asia/Shanghai", + "North Asia East Standard Time": "Asia/Irkutsk", + "Singapore Standard Time": "Asia/Singapore", + "W. Australia Standard Time": "Australia/Perth", + "Taipei Standard Time": "Asia/Taipei", + "Ulaanbaatar Standard Time": "Asia/Ulaanbaatar", + "Aus Central W. Standard Time": "Australia/Eucla", + "Transbaikal Standard Time": "Asia/Chita", + "Tokyo Standard Time": "Asia/Tokyo", + "North Korea Standard Time": "Asia/Pyongyang", + "Korea Standard Time": "Asia/Seoul", + "Yakutsk Standard Time": "Asia/Yakutsk", + "Cen. Australia Standard Time": "Australia/Adelaide", + "AUS Central Standard Time": "Australia/Darwin", + "E. Australia Standard Time": "Australia/Brisbane", + "AUS Eastern Standard Time": "Australia/Sydney", + "West Pacific Standard Time": "Pacific/Port_Moresby", + "Tasmania Standard Time": "Australia/Hobart", + "Vladivostok Standard Time": "Asia/Vladivostok", + "Lord Howe Standard Time": "Australia/Lord_Howe", + "Bougainville Standard Time": "Pacific/Bougainville", + "Russia Time Zone 10": "Asia/Srednekolymsk", + "Magadan Standard Time": "Asia/Magadan", + "Norfolk Standard Time": "Pacific/Norfolk", + "Sakhalin Standard Time": "Asia/Sakhalin", + "Central Pacific Standard Time": "Pacific/Guadalcanal", + "Russia Time Zone 11": "Asia/Kamchatka", + "New Zealand Standard Time": "Pacific/Auckland", + "UTC+12": "Etc/GMT-12", + "Fiji Standard Time": "Pacific/Fiji", + "Chatham Islands Standard Time": "Pacific/Chatham", + "UTC+13": "Etc/GMT-13", + "Tonga Standard Time": "Pacific/Tongatapu", + "Samoa Standard Time": "Pacific/Apia", + "Line Islands Standard Time": "Pacific/Kiritimati", +}; + +/** + * Resolves an MP-provided time zone identifier to an IANA name. Accepts either a + * Windows zone (MP's typical output, e.g. "Eastern Standard Time") or an IANA + * name already (e.g. "America/New_York"). Throws if the value is unknown so + * callers fail fast rather than silently drift to the server's local zone. + */ +export function resolveIanaTimezone(timeZone: string): string { + if (!timeZone || typeof timeZone !== "string") { + throw new Error("Time zone identifier is required"); + } + const trimmed = timeZone.trim(); + if (trimmed.length === 0) { + throw new Error("Time zone identifier is required"); + } + if (trimmed === "UTC" || trimmed === "Etc/UTC") { + return "Etc/UTC"; + } + if (trimmed.includes("/")) { + return trimmed; + } + const mapped = WINDOWS_TO_IANA[trimmed]; + if (!mapped) { + throw new Error( + `Unknown time zone "${trimmed}" — add it to the Windows→IANA mapping in domainTimezoneService.ts` + ); + } + return mapped; +} + +function parseWallClockParts(value: string): { + year: number; + month: number; + day: number; + hour: number; + minute: number; + second: number; +} | null { + const trimmed = value.trim(); + if (/Z$/.test(trimmed) || /[+-]\d{2}:?\d{2}$/.test(trimmed)) { + return null; + } + const match = trimmed.match( + /^(\d{4})-(\d{2})-(\d{2})(?:[T ](\d{2}):(\d{2})(?::(\d{2}))?(?:\.\d+)?)?$/ + ); + if (!match) { + return null; + } + const [, y, mo, d, h = "00", mi = "00", s = "00"] = match; + return { + year: Number(y), + month: Number(mo), + day: Number(d), + hour: Number(h), + minute: Number(mi), + second: Number(s), + }; +} + +function formatInstantAsMpSql(instant: Date, ianaTimeZone: string): string { + const parts = new Intl.DateTimeFormat("en-CA", { + timeZone: ianaTimeZone, + year: "numeric", + month: "2-digit", + day: "2-digit", + hour: "2-digit", + minute: "2-digit", + second: "2-digit", + hour12: false, + }).formatToParts(instant); + const lookup: Record = {}; + for (const part of parts) { + lookup[part.type] = part.value; + } + // Some ICU builds emit "24" for midnight under hour12:false; normalize. + const hour = lookup.hour === "24" ? "00" : lookup.hour; + return `${lookup.year}-${lookup.month}-${lookup.day} ${hour}:${lookup.minute}:${lookup.second}`; +} + +/** + * DomainTimezoneService — singleton helper for converting date/time values + * between MP's domain time zone and the application's various surfaces. + * + * Why this exists: MP stores datetimes as wall-clock values in the domain's + * configured time zone (NOT UTC). Sending a UTC-tagged value or letting + * `new Date(...).getFullYear()` round-trip through the server's local time + * silently shifts dates by the offset between server and MP. + */ +export class DomainTimezoneService { + private static instance: DomainTimezoneService | null = null; + private mp: MPHelper; + private cachedIana: string | null = null; + private inflight: Promise | null = null; + + private constructor() { + this.mp = new MPHelper(); + } + + public static getInstance(): DomainTimezoneService { + if (!DomainTimezoneService.instance) { + DomainTimezoneService.instance = new DomainTimezoneService(); + } + return DomainTimezoneService.instance; + } + + public async getMpTimezone(): Promise { + if (this.cachedIana) { + return this.cachedIana; + } + if (!this.inflight) { + this.inflight = (async () => { + const info = await this.mp.getDomainInfo(); + const iana = resolveIanaTimezone(info.TimeZoneName); + this.cachedIana = iana; + return iana; + })().finally(() => { + this.inflight = null; + }); + } + return this.inflight; + } + + /** + * Converts a value into the SQL datetime string MP's table API expects + * ("YYYY-MM-DD HH:MM:SS" in the MP domain's wall-clock time). + * + * - Wall-clock string with no zone marker → reformatted as MP-TZ wall-clock, + * missing components default to zero. + * - String with trailing "Z" or "±HH:MM" offset → parsed as a UTC/offset + * instant and converted into MP-TZ wall-clock. + * - `Date` instances → converted as UTC instants. + */ + public async toMpSqlDatetime(value: Date | string): Promise { + if (value instanceof Date) { + const iana = await this.getMpTimezone(); + return formatInstantAsMpSql(value, iana); + } + if (typeof value !== "string" || value.trim().length === 0) { + throw new Error("toMpSqlDatetime: value must be a non-empty string or Date"); + } + const wallClock = parseWallClockParts(value); + if (wallClock) { + const pad = (n: number) => String(n).padStart(2, "0"); + return `${wallClock.year}-${pad(wallClock.month)}-${pad(wallClock.day)} ${pad(wallClock.hour)}:${pad(wallClock.minute)}:${pad(wallClock.second)}`; + } + const parsed = new Date(value); + if (Number.isNaN(parsed.getTime())) { + throw new Error(`toMpSqlDatetime: unable to parse "${value}"`); + } + const iana = await this.getMpTimezone(); + return formatInstantAsMpSql(parsed, iana); + } + + /** + * Parses an MP wall-clock datetime string into a `Date` instant. Use when + * you need real arithmetic on values returned from MP — for display, prefer + * `Intl.DateTimeFormat({ timeZone })` directly against the raw string. + */ + public async parseMpDatetime(value: string): Promise { + const wallClock = parseWallClockParts(value); + if (!wallClock) { + const direct = new Date(value); + if (Number.isNaN(direct.getTime())) { + throw new Error(`parseMpDatetime: unable to parse "${value}"`); + } + return direct; + } + const iana = await this.getMpTimezone(); + const utcGuess = Date.UTC( + wallClock.year, + wallClock.month - 1, + wallClock.day, + wallClock.hour, + wallClock.minute, + wallClock.second + ); + const projected = formatInstantAsMpSql(new Date(utcGuess), iana); + const projectedParts = parseWallClockParts(projected)!; + const projectedUtc = Date.UTC( + projectedParts.year, + projectedParts.month - 1, + projectedParts.day, + projectedParts.hour, + projectedParts.minute, + projectedParts.second + ); + const offset = utcGuess - projectedUtc; + return new Date(utcGuess + offset); + } + + /** Test hook — clears cached domain info so the next call refetches. */ + public clearCache(): void { + this.cachedIana = null; + this.inflight = null; + } +} + +export const domainTimezoneService = DomainTimezoneService.getInstance(); diff --git a/src/services/fullCalendarService.test.ts b/src/services/fullCalendarService.test.ts index 4954e48..59d8e2e 100644 --- a/src/services/fullCalendarService.test.ts +++ b/src/services/fullCalendarService.test.ts @@ -1,25 +1,38 @@ -import { describe, it, expect, vi, beforeEach } from 'vitest'; -import { FullCalendarService } from '@/services/fullCalendarService'; +import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; -const mockGetTableRecords = vi.fn(); -const mockGetFilesByRecord = vi.fn(); +const { mockGetTableRecords, mockGetFilesByRecord, mockGetDomainInfo } = vi.hoisted(() => ({ + mockGetTableRecords: vi.fn(), + mockGetFilesByRecord: vi.fn(), + mockGetDomainInfo: vi.fn(), +})); vi.mock('@/lib/providers/ministry-platform', () => { return { MPHelper: class { getTableRecords = mockGetTableRecords; getFilesByRecord = mockGetFilesByRecord; + getDomainInfo = mockGetDomainInfo; }, }; }); +import { FullCalendarService } from '@/services/fullCalendarService'; +import { DomainTimezoneService } from '@/services/domainTimezoneService'; + describe('FullCalendarService', () => { beforeEach(() => { - vi.clearAllMocks(); + // mockReset (not clearAllMocks) so mockResolvedValueOnce queues drain. + mockGetTableRecords.mockReset(); + mockGetFilesByRecord.mockReset(); + mockGetDomainInfo.mockReset(); // eslint-disable-next-line @typescript-eslint/no-explicit-any (FullCalendarService as any).instance = undefined; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (DomainTimezoneService as any).instance = null; // Default: getFilesByRecord returns no images mockGetFilesByRecord.mockResolvedValue([]); + // Default domain: Eastern. Individual tests may override before exercising. + mockGetDomainInfo.mockResolvedValue({ TimeZoneName: 'America/New_York' }); }); describe('getInstance', () => { @@ -232,6 +245,35 @@ describe('FullCalendarService', () => { const service = await FullCalendarService.getInstance(); await expect(service.getEvents(start, end)).rejects.toThrow('events failed'); }); + + it('builds the $filter literal as MP-TZ wall-clock (Recipe E — no UTC shift)', async () => { + mockGetTableRecords.mockResolvedValueOnce([]); + + const service = await FullCalendarService.getInstance(); + await service.getEvents('2026-05-01T00:00:00Z', '2026-05-31T23:59:59Z'); + + const filter = mockGetTableRecords.mock.calls[0][0].filter as string; + // 2026-05-01T00:00:00Z → 2026-04-30 20:00:00 in America/New_York (EDT, UTC-4) + expect(filter).toContain(`Event_Start_Date >= '2026-04-30 20:00:00'`); + expect(filter).toContain(`Event_End_Date <= '2026-05-31 19:59:59'`); + }); + + it('regression: repeating the same getEvents call produces the same filter (no drift)', async () => { + mockGetTableRecords.mockResolvedValue([]); + + const service = await FullCalendarService.getInstance(); + await service.getEvents('2026-05-01T00:00:00Z', '2026-05-31T23:59:59Z'); + await service.getEvents('2026-05-01T00:00:00Z', '2026-05-31T23:59:59Z'); + await service.getEvents('2026-05-01T00:00:00Z', '2026-05-31T23:59:59Z'); + + // The filter substring under inspection must be stable across calls, + // regardless of how many times getDomainInfo is hit or what TZ the + // node process is running in. + const filters = mockGetTableRecords.mock.calls.map((c) => c[0].filter as string); + expect(filters[0]).toContain(`Event_Start_Date >= '2026-04-30 20:00:00'`); + expect(filters[1]).toBe(filters[0]); + expect(filters[2]).toBe(filters[0]); + }); }); describe('getEventDetail', () => { diff --git a/src/services/fullCalendarService.ts b/src/services/fullCalendarService.ts index 1bc7460..431c536 100644 --- a/src/services/fullCalendarService.ts +++ b/src/services/fullCalendarService.ts @@ -1,4 +1,5 @@ import { MPHelper } from "@/lib/providers/ministry-platform"; +import { DomainTimezoneService } from "@/services/domainTimezoneService"; import type { CalendarEvent } from "@mpnext/types"; // ── MP Record Types ── @@ -119,9 +120,12 @@ export class FullCalendarService { congregationId?: number, userGuid?: string ): Promise<{ events: CalendarEvent[]; isAdmin: boolean; filters: FilterData }> { - // MP/SQL Server needs simple datetime format — strip timezone offsets - const startDate = new Date(start).toISOString().slice(0, 19).replace("T", " "); - const endDate = new Date(end).toISOString().slice(0, 19).replace("T", " "); + // MP $filter literals are interpreted in the domain's wall-clock time zone. + // Routing through DomainTimezoneService converts any incoming instant (Z or + // offset-tagged) to MP-TZ wall-clock so date-boundary queries don't shift. + const tz = DomainTimezoneService.getInstance(); + const startDate = await tz.toMpSqlDatetime(start); + const endDate = await tz.toMpSqlDatetime(end); let filter = `Event_Start_Date >= '${startDate}' AND Event_End_Date <= '${endDate}' AND Cancelled = 0 AND Visibility_Level_ID = 4`; if (congregationId) { From dad2296517f5c4f6ec9fde4f0649d36b015cc293 Mon Sep 17 00:00:00 2001 From: Chris Kehayias Date: Sat, 30 May 2026 16:09:17 -0400 Subject: [PATCH 02/20] refactor: eliminate hardcoded Northwoods references Remove all hardcoded 'northwoods' tenant identifiers, my.northwoods.church hosts, and Northwoods-specific group ID defaults so the codebase is tenant-neutral. - MP host: server resolves via getMpHost() (throws if MINISTRY_PLATFORM_BASE_URL unset); docs/snippets use neutral placeholder; widget falls back soft with a warning - SDK build: drop church default, inject org name via VITE_ORG_NAME build define - profile widget SMS consent text uses configurable org name - Demo HTML: use existing __MP_BASE_URL__ placeholder; delete dead tid/initToken (session auth is origin-based) - Group gates fail closed: remove '22'/'73' defaults; unset = no access + warning - Docs: drop NorthwoodsNext/Northwoods-specific wording; fix stale CLAUDE.md tenant note - Tests: neutral fixtures + fail-closed coverage Co-Authored-By: Claude Opus 4.8 (1M context) --- .claude/references/ministryplatform.schema.md | 2 +- .env.example | 13 ++++++--- CLAUDE.md | 4 +-- README.md | 2 +- packages/embed-sdk/demo-my-invoices.html | 8 ++---- packages/embed-sdk/demo-profile.html | 8 ++---- packages/embed-sdk/src/components/profile.ts | 9 +++++- .../embed-sdk/src/components/user-menu.ts | 12 +++++++- .../embed-sdk/src/shared/api-client.test.ts | 4 +-- packages/embed-sdk/vite.config.ts | 13 +++++++-- src/app/(demo)/demo/[slug]/page.tsx | 4 +-- src/app/(demo)/demo/_lib/check-demo-access.ts | 16 +++++++++-- src/app/(demo)/demo/_lib/widget-catalog.ts | 5 ++-- src/lib/embed/config.ts | 27 ++++++++++++++++++ .../scripts/generate-types.ts | 2 +- src/services/fullCalendarService.test.ts | 28 +++++++++++++++++++ src/services/fullCalendarService.ts | 11 ++++++-- 17 files changed, 135 insertions(+), 33 deletions(-) diff --git a/.claude/references/ministryplatform.schema.md b/.claude/references/ministryplatform.schema.md index 1510060..42ee673 100644 --- a/.claude/references/ministryplatform.schema.md +++ b/.claude/references/ministryplatform.schema.md @@ -1,6 +1,6 @@ # Ministry Platform Schema Reference -This document provides a summary of Ministry Platform database tables for LLM assistants working on the NorthwoodsNext project. +This document provides a summary of Ministry Platform database tables for LLM assistants working on this project. **Generated:** 2026-03-25T14:07:09.466Z **Tables:** 301 diff --git a/.env.example b/.env.example index cb7dfd6..2b1dd74 100644 --- a/.env.example +++ b/.env.example @@ -18,6 +18,11 @@ MINISTRY_PLATFORM_BASE_URL=https://your-mp-instance.com/ministryplatformapi MINISTRY_PLATFORM_CLIENT_ID=your-client-id MINISTRY_PLATFORM_CLIENT_SECRET=your-client-secret +# Organization display name baked into the embed SDK at build time (e.g. the +# SMS opt-in consent text in the profile widget). Unset falls back to a neutral +# phrase ("our organization"). +VITE_ORG_NAME= + # ============================================================================ # Public Keys (exposed to the browser) # ============================================================================ @@ -45,8 +50,8 @@ RECAPTCHA_SECRET_KEY= # Demo Access Control (src/app/(demo)/...) # ============================================================================ # Comma-separated MP User_Group_ID values whose members can view /demo pages. -# Defaults to "73" in code, which is a Northwoods-specific group ID — set this -# to your own staff/admin group(s) for any other tenant. +# No default — set this to your own staff/admin group(s). If unset (and +# DEMO_PUBLIC_ACCESS is not enabled), group-based demo access is denied. DEMO_ACCESS_GROUP_IDS= # Set to "true" or "authenticated" to grant any signed-in user access to /demo @@ -57,8 +62,8 @@ DEMO_PUBLIC_ACCESS= # Full Calendar Admin (src/services/fullCalendarService.ts) # ============================================================================ # Comma-separated MP User_Group_ID values whose members are treated as -# calendar admins. Defaults to "22" in code (Northwoods-specific) — override -# for your tenant. +# calendar admins. No default — set this for your tenant. If unset, no users +# are treated as calendar admins. CALENDAR_ADMIN_GROUP_IDS= # ============================================================================ diff --git a/CLAUDE.md b/CLAUDE.md index 0b5a5b8..2bb9d1d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -2,7 +2,7 @@ ## Overview -**pnpm monorepo**: Component-only extraction from NorthwoodsNext. Contains 3 embed SDK widgets (user-menu, add-to-calendar, full-calendar) with their supporting API routes, services, and shared types. The embed SDK builds framework-agnostic Web Components (Shadow DOM) loaded via ` + in stringify output) pnpm audit now reports 0 vulnerabilities. Safe updates: @types/react 19.2.15, vitest/@vitest/coverage-v8 4.1.7, better-auth 1.6.12, postcss 8.5.15, react-hook-form 7.76.1, tsx 4.22.3, typescript 6.0.3, vite 8.0.14, @hookform/resolvers 5.4.0, @inquirer/prompts 8.5.1, @types/node 25.9.1, lucide-react 1.17.0, zod aligned to 4.4.3 in @mpnext/types. Workaround: pin kysely ^0.28.17 in overrides. better-auth 1.6.12 widened its kysely peer range to ^0.29.0, but @better-auth/kysely-adapter's dist still imports DEFAULT_MIGRATION_TABLE/DEFAULT_MIGRATION_LOCK_TABLE, which kysely 0.29 removed — broke the Next build. Remove once upstream ships a 0.29-compatible adapter. Verified: pnpm build (SDK + Next) green, pnpm lint clean, pnpm test:run 530/530 passing. Co-Authored-By: Claude Opus 4.8 (1M context) --- package.json | 26 +- packages/embed-sdk/package.json | 2 +- packages/types/package.json | 2 +- pnpm-lock.yaml | 1370 ++++++++++++++++--------------- 4 files changed, 719 insertions(+), 681 deletions(-) diff --git a/package.json b/package.json index 7c6357c..f6f6f83 100644 --- a/package.json +++ b/package.json @@ -29,45 +29,47 @@ }, "dependencies": { "@heroicons/react": "^2.2.0", - "@hookform/resolvers": "^5.0.1", + "@hookform/resolvers": "^5.4.0", "@radix-ui/react-dialog": "^1.1.14", "@radix-ui/react-dropdown-menu": "^2.1.15", "@radix-ui/react-label": "^2.1.7", "@radix-ui/react-select": "^2.2.5", "@radix-ui/react-slot": "^1.2.3", "@radix-ui/react-tabs": "^1.1.13", - "better-auth": "^1.6.11", + "better-auth": "^1.6.12", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "dotenv": "^17.4.2", "jose": "^6.2.3", - "lucide-react": "^1.16.0", + "lucide-react": "^1.17.0", "next": "^16.2.6", "react": "^19.2.6", "react-dom": "^19.2.6", - "react-hook-form": "^7.76.0", + "react-hook-form": "^7.76.1", "tailwind-merge": "^3.6.0", - "tsx": "^4.22.1", + "tsx": "^4.22.3", "zod": "^4.4.3" }, "pnpm": { "overrides": { "cookie": ">=0.7.0", "defu": ">=6.1.7", - "minimatch@3>brace-expansion": "1.1.12" + "minimatch@3>brace-expansion": "1.1.15", + "postcss": ">=8.5.10", + "kysely": "^0.28.17" } }, "devDependencies": { - "@inquirer/prompts": "^8.4.3", + "@inquirer/prompts": "^8.5.1", "@playwright/test": "^1.60.0", "@tailwindcss/postcss": "^4.3.0", "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", - "@types/node": "^25.8.0", - "@types/react": "^19.2.14", + "@types/node": "^25.9.1", + "@types/react": "^19.2.15", "@types/react-dom": "^19", "@vitejs/plugin-react": "^6.0.2", - "@vitest/coverage-v8": "^4.1.0", + "@vitest/coverage-v8": "^4.1.7", "autoprefixer": "^10.5.0", "chalk": "^5.6.2", "concurrently": "^9.2.1", @@ -75,11 +77,11 @@ "eslint-config-next": "^16.2.6", "jsdom": "^29.0.0", "playwright": "^1.60.0", - "postcss": "^8.5.14", + "postcss": "^8.5.15", "tailwindcss": "^4.3.0", "tw-animate-css": "^1.3.0", "typescript": "^6.0.3", - "vitest": "^4.1.0" + "vitest": "^4.1.7" }, "packageManager": "pnpm@10.29.3+sha512.498e1fb4cca5aa06c1dcf2611e6fafc50972ffe7189998c409e90de74566444298ffe43e6cd2acdc775ba1aa7cc5e092a8b7054c811ba8c5770f84693d33d2dc" } diff --git a/packages/embed-sdk/package.json b/packages/embed-sdk/package.json index c2bf814..cc0be53 100644 --- a/packages/embed-sdk/package.json +++ b/packages/embed-sdk/package.json @@ -22,6 +22,6 @@ "devDependencies": { "@types/node": "^20.17.10", "typescript": "^6", - "vite": "^8.0.13" + "vite": "^8.0.14" } } diff --git a/packages/types/package.json b/packages/types/package.json index cb36ece..dbe966e 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -20,6 +20,6 @@ "typescript": "^6" }, "dependencies": { - "zod": "^4.3.6" + "zod": "^4.4.3" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d26cb08..6809a1b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,7 +7,9 @@ settings: overrides: cookie: '>=0.7.0' defu: '>=6.1.7' - minimatch@3>brace-expansion: 1.1.12 + minimatch@3>brace-expansion: 1.1.15 + postcss: '>=8.5.10' + kysely: ^0.28.17 importers: @@ -17,29 +19,29 @@ importers: specifier: ^2.2.0 version: 2.2.0(react@19.2.6) '@hookform/resolvers': - specifier: ^5.0.1 - version: 5.2.2(react-hook-form@7.76.0(react@19.2.6)) + specifier: ^5.4.0 + version: 5.4.0(react-hook-form@7.76.1(react@19.2.6)) '@radix-ui/react-dialog': specifier: ^1.1.14 - version: 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + version: 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@radix-ui/react-dropdown-menu': specifier: ^2.1.15 - version: 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + version: 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@radix-ui/react-label': specifier: ^2.1.7 - version: 2.1.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + version: 2.1.8(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@radix-ui/react-select': specifier: ^2.2.5 - version: 2.2.6(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + version: 2.2.6(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@radix-ui/react-slot': specifier: ^1.2.3 - version: 1.2.4(@types/react@19.2.14)(react@19.2.6) + version: 1.2.4(@types/react@19.2.15)(react@19.2.6) '@radix-ui/react-tabs': specifier: ^1.1.13 - version: 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + version: 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) better-auth: - specifier: ^1.6.11 - version: 1.6.11(@opentelemetry/api@1.9.0)(next@16.2.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.60.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.6) + specifier: ^1.6.12 + version: 1.6.12(@opentelemetry/api@1.9.0)(next@16.2.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.60.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) class-variance-authority: specifier: ^0.7.1 version: 0.7.1 @@ -53,8 +55,8 @@ importers: specifier: ^6.2.3 version: 6.2.3 lucide-react: - specifier: ^1.16.0 - version: 1.16.0(react@19.2.6) + specifier: ^1.17.0 + version: 1.17.0(react@19.2.6) next: specifier: ^16.2.6 version: 16.2.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.60.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) @@ -65,21 +67,21 @@ importers: specifier: ^19.2.6 version: 19.2.6(react@19.2.6) react-hook-form: - specifier: ^7.76.0 - version: 7.76.0(react@19.2.6) + specifier: ^7.76.1 + version: 7.76.1(react@19.2.6) tailwind-merge: specifier: ^3.6.0 version: 3.6.0 tsx: - specifier: ^4.22.1 - version: 4.22.1 + specifier: ^4.22.3 + version: 4.22.3 zod: specifier: ^4.4.3 version: 4.4.3 devDependencies: '@inquirer/prompts': - specifier: ^8.4.3 - version: 8.4.3(@types/node@25.8.0) + specifier: ^8.5.1 + version: 8.5.1(@types/node@25.9.1) '@playwright/test': specifier: ^1.60.0 version: 1.60.0 @@ -91,25 +93,25 @@ importers: version: 6.9.1 '@testing-library/react': specifier: ^16.3.2 - version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@types/node': - specifier: ^25.8.0 - version: 25.8.0 + specifier: ^25.9.1 + version: 25.9.1 '@types/react': - specifier: ^19.2.14 - version: 19.2.14 + specifier: ^19.2.15 + version: 19.2.15 '@types/react-dom': specifier: ^19 - version: 19.2.3(@types/react@19.2.14) + version: 19.2.3(@types/react@19.2.15) '@vitejs/plugin-react': specifier: ^6.0.2 - version: 6.0.2(vite@8.0.13(@types/node@25.8.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.1)) + version: 6.0.2(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.3)) '@vitest/coverage-v8': - specifier: ^4.1.0 - version: 4.1.6(vitest@4.1.6) + specifier: ^4.1.7 + version: 4.1.7(vitest@4.1.7) autoprefixer: specifier: ^10.5.0 - version: 10.5.0(postcss@8.5.14) + version: 10.5.0(postcss@8.5.15) chalk: specifier: ^5.6.2 version: 5.6.2 @@ -124,13 +126,13 @@ importers: version: 16.2.6(@typescript-eslint/parser@8.57.2(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3) jsdom: specifier: ^29.0.0 - version: 29.1.1(@noble/hashes@2.0.1) + version: 29.1.1(@noble/hashes@2.2.0) playwright: specifier: ^1.60.0 version: 1.60.0 postcss: - specifier: ^8.5.14 - version: 8.5.14 + specifier: '>=8.5.10' + version: 8.5.15 tailwindcss: specifier: ^4.3.0 version: 4.3.0 @@ -141,8 +143,8 @@ importers: specifier: ^6.0.3 version: 6.0.3 vitest: - specifier: ^4.1.0 - version: 4.1.6(@opentelemetry/api@1.9.0)(@types/node@25.8.0)(@vitest/coverage-v8@4.1.6)(jsdom@29.1.1(@noble/hashes@2.0.1))(vite@8.0.13(@types/node@25.8.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.1)) + specifier: ^4.1.7 + version: 4.1.7(@opentelemetry/api@1.9.0)(@types/node@25.9.1)(@vitest/coverage-v8@4.1.7)(jsdom@29.1.1(@noble/hashes@2.2.0))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.3)) packages/embed-sdk: devDependencies: @@ -153,14 +155,14 @@ importers: specifier: ^6 version: 6.0.2 vite: - specifier: ^8.0.13 - version: 8.0.13(@types/node@20.19.37)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.1) + specifier: ^8.0.14 + version: 8.0.14(@types/node@20.19.37)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.3) packages/types: dependencies: zod: - specifier: ^4.3.6 - version: 4.3.6 + specifier: ^4.4.3 + version: 4.4.3 devDependencies: typescript: specifier: ^6 @@ -194,6 +196,10 @@ packages: resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} engines: {node: '>=6.9.0'} + '@babel/code-frame@7.29.7': + resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} + engines: {node: '>=6.9.0'} + '@babel/compat-data@7.29.0': resolution: {integrity: sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==} engines: {node: '>=6.9.0'} @@ -228,10 +234,18 @@ packages: resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} engines: {node: '>=6.9.0'} + '@babel/helper-string-parser@7.29.7': + resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} + engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@7.28.5': resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} + engines: {node: '>=6.9.0'} + '@babel/helper-validator-option@7.27.1': resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} engines: {node: '>=6.9.0'} @@ -245,8 +259,8 @@ packages: engines: {node: '>=6.0.0'} hasBin: true - '@babel/parser@7.29.3': - resolution: {integrity: sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==} + '@babel/parser@7.29.7': + resolution: {integrity: sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==} engines: {node: '>=6.0.0'} hasBin: true @@ -254,6 +268,10 @@ packages: resolution: {integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==} engines: {node: '>=6.9.0'} + '@babel/runtime@7.29.7': + resolution: {integrity: sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==} + engines: {node: '>=6.9.0'} + '@babel/template@7.28.6': resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==} engines: {node: '>=6.9.0'} @@ -266,20 +284,24 @@ packages: resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} engines: {node: '>=6.9.0'} + '@babel/types@7.29.7': + resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==} + engines: {node: '>=6.9.0'} + '@bcoe/v8-coverage@1.0.2': resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} engines: {node: '>=18'} - '@better-auth/core@1.6.11': - resolution: {integrity: sha512-LrwidLCV8azdMGjvtwp30nj9tIv1BwI3VhtC0UaGSjQkAVWw4bN42I8qwbxRziPeSQoj+zUVkOpxZzAWBDARtQ==} + '@better-auth/core@1.6.12': + resolution: {integrity: sha512-6mXtYSYfo6TvHHCZAZmfjvIQQtBDWzWzwy9iIWPEoede2lP2SuJzkfIQNuTtIGzZcn7a9iuzIm1jWDBzfnBARg==} peerDependencies: - '@better-auth/utils': 0.4.0 + '@better-auth/utils': 0.4.1 '@better-fetch/fetch': 1.1.21 '@cloudflare/workers-types': '>=4' '@opentelemetry/api': ^1.9.0 better-call: 1.3.5 jose: ^6.1.0 - kysely: ^0.28.5 + kysely: ^0.28.17 nanostores: ^1.0.1 peerDependenciesMeta: '@cloudflare/workers-types': @@ -287,47 +309,47 @@ packages: '@opentelemetry/api': optional: true - '@better-auth/drizzle-adapter@1.6.11': - resolution: {integrity: sha512-4jpkETIGZOHCf7BK4jnu22fdN6jjomH0/HhEzkaWy3+Eppi5PYlHTF/460jrTmA3Xc+Vqwp9t282ymHiEPypGw==} + '@better-auth/drizzle-adapter@1.6.12': + resolution: {integrity: sha512-g0sKQstvXHH70s+TjAXo86cNyWV60ahhJm1sow27RyW41U10vfBehOFinU3GPESyxl/fEr9D27rk3jdl6E3l3A==} peerDependencies: - '@better-auth/core': ^1.6.11 - '@better-auth/utils': 0.4.0 + '@better-auth/core': ^1.6.12 + '@better-auth/utils': 0.4.1 drizzle-orm: ^0.45.2 peerDependenciesMeta: drizzle-orm: optional: true - '@better-auth/kysely-adapter@1.6.11': - resolution: {integrity: sha512-/g8M9RfIjdcZDnbstSUvQiINkvdNlCeZr248zwqx2/PVksQI1MhQofbzUn3RnQnbPKp0EPwpX/dR3oudRFenUg==} + '@better-auth/kysely-adapter@1.6.12': + resolution: {integrity: sha512-KhPwPmLj+MoTVGV6goPfCYf/7Fuiy2Q37GEWhvQdoFjkYKbGo995OoghBVNBnAYOakYvTYjG0JebCfiETBVX3g==} peerDependencies: - '@better-auth/core': ^1.6.11 - '@better-auth/utils': 0.4.0 + '@better-auth/core': ^1.6.12 + '@better-auth/utils': 0.4.1 kysely: ^0.28.17 peerDependenciesMeta: kysely: optional: true - '@better-auth/memory-adapter@1.6.11': - resolution: {integrity: sha512-hpdfw0BBf8MuzLkIdmbcUZICbY9r/bhLO2RxSnkzT5+/O+0I0u2I8+m0YUP7vNllP/ZCKASHOYgXPLO75Z0f9Q==} + '@better-auth/memory-adapter@1.6.12': + resolution: {integrity: sha512-flblsePBCcB0DA6hewAOupxyypNTQczZvkNYvRrsVlBDIh0+vHBU/dTjoDmuQnZ3egTdFNnMeC+VrNnqt/GFUg==} peerDependencies: - '@better-auth/core': ^1.6.11 - '@better-auth/utils': 0.4.0 + '@better-auth/core': ^1.6.12 + '@better-auth/utils': 0.4.1 - '@better-auth/mongo-adapter@1.6.11': - resolution: {integrity: sha512-3Tor8rSv8vSEIMEaV2PFpPEuVhqc1gNoZ6eGvoh3LwExXXuj8madew6ob+H1pH7Aphn3Ar5PQ08AguT8TbwFAA==} + '@better-auth/mongo-adapter@1.6.12': + resolution: {integrity: sha512-IeiHZN9PtIyiqYgTDlrmm8sYI++5p1OI49uWB7LHg2+touiaNUGe0uWYymQpw1zq1e8FJxKlwvOc5vw6nGrI6g==} peerDependencies: - '@better-auth/core': ^1.6.11 - '@better-auth/utils': 0.4.0 + '@better-auth/core': ^1.6.12 + '@better-auth/utils': 0.4.1 mongodb: ^6.0.0 || ^7.0.0 peerDependenciesMeta: mongodb: optional: true - '@better-auth/prisma-adapter@1.6.11': - resolution: {integrity: sha512-Pw+7q7zTp+VSci1V+CYMvuxIbAeVMZLe4lRo46LJoAKMHfjFl5T/ycsyFvWs/DkWC7n9gZZzRDEbHp0I5FiKKw==} + '@better-auth/prisma-adapter@1.6.12': + resolution: {integrity: sha512-+GvU8vZ3aJUHDBuR5PxtU5OpPQS2T9ND7s2JYm63bD6rnYztLwEo8bwHL3BvsTwSvCjFHZCtsn1A+6qyoOzTMw==} peerDependencies: - '@better-auth/core': ^1.6.11 - '@better-auth/utils': 0.4.0 + '@better-auth/core': ^1.6.12 + '@better-auth/utils': 0.4.1 '@prisma/client': ^5.0.0 || ^6.0.0 || ^7.0.0 prisma: ^5.0.0 || ^6.0.0 || ^7.0.0 peerDependenciesMeta: @@ -336,15 +358,15 @@ packages: prisma: optional: true - '@better-auth/telemetry@1.6.11': - resolution: {integrity: sha512-hsjDHc8MZbm6/AHeNdtywrWedXevnBjmdvnHTcZub+rTVjOv+Td0roI8USKuC6uUibmrl//2rJfVCsGbopihNA==} + '@better-auth/telemetry@1.6.12': + resolution: {integrity: sha512-g59qLPq9SROyku0X5tiZpXXiVrsbjB1QA6OctOt9svzj7NjCFBoCAO9QlBiOTUolo0l9CF6fLlc85PoBkY5RtA==} peerDependencies: - '@better-auth/core': ^1.6.11 - '@better-auth/utils': 0.4.0 + '@better-auth/core': ^1.6.12 + '@better-auth/utils': 0.4.1 '@better-fetch/fetch': 1.1.21 - '@better-auth/utils@0.4.0': - resolution: {integrity: sha512-RpMtLUIQAEWMgdPLNVbIF5ON2mm+CH0U3rCdUCU1VyeAUui4m38DyK7/aXMLZov2YDjG684pS1D0MBllrmgjQA==} + '@better-auth/utils@0.4.1': + resolution: {integrity: sha512-SZBPRPF3z0nBvE5ygOkxae35wnnXPRShmqFo78S+qslLeFoPu/pMgnXAuNKFMMybac3tiLaVg1e3MQW5MC+1iA==} '@better-fetch/fetch@1.1.21': resolution: {integrity: sha512-/ImESw0sskqlVR94jB+5+Pxjf+xBwDZF/N5+y2/q4EqD7IARUTSpPfIo8uf39SYpCxyOCtbyYpUrZ3F/k0zT4A==} @@ -630,8 +652,8 @@ packages: peerDependencies: react: '>= 16 || ^19.0.0-rc' - '@hookform/resolvers@5.2.2': - resolution: {integrity: sha512-A/IxlMLShx3KjV/HeTcTfaMxdwy690+L/ZADoeaTltLx+CVuzkeVIPuybK3jrRfw7YZnmdKsVVHAlEPIAEUNlA==} + '@hookform/resolvers@5.4.0': + resolution: {integrity: sha512-EIsqr/t/qbinPIhGjMdtvutIN1Kk4uwbROE9/UQ93CAVGR7GkA7Y92+fX80OzXi/OB67jVFYwKGO1WzkxmkFZw==} peerDependencies: react-hook-form: ^7.55.0 @@ -804,134 +826,134 @@ packages: cpu: [x64] os: [win32] - '@inquirer/ansi@2.0.5': - resolution: {integrity: sha512-doc2sWgJpbFQ64UflSVd17ibMGDuxO1yKgOgLMwavzESnXjFWJqUeG8saYosqKpHp4kWiM5x1nXvEjbpx90gzw==} - engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + '@inquirer/ansi@2.0.7': + resolution: {integrity: sha512-3eTuUO1vH2cZm2ZKHeQxnOqlTi9EfZDGgIe3BL3I4u+rJHocr9Fz86M4fjYABPvFnQG/gGK551HqDiIcETwU6Q==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} - '@inquirer/checkbox@5.1.5': - resolution: {integrity: sha512-Jmf9tgBHIEK5SAOB7swYfStqmtkZb00xOTpSQmkoGEpdxOTpJi9RS0A8bkfDPHTTItZRJrRdZrEMu25wyj0VfQ==} - engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + '@inquirer/checkbox@5.2.1': + resolution: {integrity: sha512-b6xmA/VlTe0ZgDQHDui+Nav470u7u49nRd8/iuhOcQPO9Ch7lGuogydhi2VOmNlZ+zXcM8IcPuNSwQcdJaF/kw==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: '@types/node': optional: true - '@inquirer/confirm@6.0.13': - resolution: {integrity: sha512-wkGPC7yJ5WJk1DJ5SX7fzk+gfj4BM8cf5dDDi71B/551xHrdsZVRJOC0WyikXd0pEsb/9cLniuE4atbsMqmFkw==} - engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + '@inquirer/confirm@6.1.1': + resolution: {integrity: sha512-eb8DBZcz/2qHWQda4rk2JiQk5h9QV/cVHi1yjt0f69WFZMRFn0sJTye3EAP8icut8UDMjQPsaH5KbcOogefrFQ==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: '@types/node': optional: true - '@inquirer/core@11.1.10': - resolution: {integrity: sha512-a4Q5BXHQAHa9eO202sTaFCHFYVB3x5fauDuThEAdZ9gfn76pSxiKU7wWcEH0N1O0XmQvNfQNU6QXpiRxmYQx+A==} - engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + '@inquirer/core@11.2.1': + resolution: {integrity: sha512-Qd6GJT1yVyrZZCfN8W2qKF5ApmqryXRhRKCuip8h01x2w/esJQ2XIYc6f9abMIHgKQdBfFTSOdbHRLAhuM09UA==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: '@types/node': optional: true - '@inquirer/editor@5.1.2': - resolution: {integrity: sha512-Y3Nor7S/DhIPo+8Ym/dSY4efwKI4BsflKDwXh0jNeXJsSF3dteS/3Yf+z4wkibVZDvYMyCgknSTQlNahfunGHg==} - engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + '@inquirer/editor@5.2.1': + resolution: {integrity: sha512-NFLxWmTPc2WORINoffcbVwB2+tujeNZ6Cu50HbXvTbk0fHSmhA5PgCckxXuyUniQvX7bhxkZi0nODfJIGsnCdA==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: '@types/node': optional: true - '@inquirer/expand@5.0.14': - resolution: {integrity: sha512-qyY9zcIX2eKYwaAUiQo9zORd61Lc3sXeM72fVbeHkYnDkqfr8/armcRbmVAIrExeJhI2puk+uomeKtWrpUVUmQ==} - engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + '@inquirer/expand@5.1.1': + resolution: {integrity: sha512-YmQpenjbFSHAK3sOd44puHh3V1KXXr+JiNpUztoSQ4drLh2rTVzTap/YtlAVu/5xavifIlBfNEzJ/neZJ1a/1g==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: '@types/node': optional: true - '@inquirer/external-editor@3.0.0': - resolution: {integrity: sha512-lDSwMgg+M5rq6JKBYaJwSX6T9e/HK2qqZ1oxmOwn4AQoJE5D+7TumsxLGC02PWS//rkIVqbZv3XA3ejsc9FYvg==} - engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + '@inquirer/external-editor@3.0.2': + resolution: {integrity: sha512-1X+KN2PinUcy/aS3f3OBSKJaWk+jBikMry4Qblj5ysqN4T+8BeA1rNJdTSOlQ2iOmvKg7ZsR4tDQvl1p0PLgKg==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: '@types/node': optional: true - '@inquirer/figures@2.0.5': - resolution: {integrity: sha512-NsSs4kzfm12lNetHwAn3GEuH317IzpwrMCbOuMIVytpjnJ90YYHNwdRgYGuKmVxwuIqSgqk3M5qqQt1cDk0tGQ==} - engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + '@inquirer/figures@2.0.7': + resolution: {integrity: sha512-aJ8TBPOGB6f/2qziPfElISTCEd5XOYTFckA2SGjhNmiKzfK/u4ot3v0DUzGVdUnKjN10EqnnEPck36BkyfLnJw==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} - '@inquirer/input@5.0.13': - resolution: {integrity: sha512-0l0jCHlJnXIV8CTxwQC0C+5Ziq8WP22edWgmciW2xYvoeoSck4v5FvCS1ctKdqLLR0dUo93uAHgWHywgBSoRyw==} - engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + '@inquirer/input@5.1.1': + resolution: {integrity: sha512-3wuHoDBBtU+o0TB4uP2R+ACdNrn6SgdZBc1YUaiU9GcfXNaTNUgSM/Ft7eQ3gYWP2H1MiT6glycmfWYpmPzVKw==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: '@types/node': optional: true - '@inquirer/number@4.0.13': - resolution: {integrity: sha512-WHmkYnnJAou5gx7RgcvAfUggnHNM1zWfoh0dFPl3dxVssuqt+dK5rIbaOYQXNyOegvFnopbKupjnhw2O8gANNg==} - engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + '@inquirer/number@4.1.1': + resolution: {integrity: sha512-XF4IXAbPnGPgw0wsbC/i2tPcyfdZgDpUlhsqU0SfT4IRIGWha6Xm9VRgN5yYxJq+jnyXlfXI/nQ3ulfk0iEICA==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: '@types/node': optional: true - '@inquirer/password@5.0.13': - resolution: {integrity: sha512-XDGu64ROHZjOOXLAANvJN7iIxWKhOSCG5VakrZ5kaScVR+snVJCFglD/hL3/677awtWcu4pXoWa280CDIYcBeg==} - engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + '@inquirer/password@5.1.1': + resolution: {integrity: sha512-3XBfF7DAsp5qeDsvN5Rd1HmbNokVvEQoUM0QLrRcybC9nX96w3Pbmu7qUsb3IT3J3jBvs2+mTXaKHOUsgHMLzg==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: '@types/node': optional: true - '@inquirer/prompts@8.4.3': - resolution: {integrity: sha512-ai5LseTw9HhegupIgmo4cn7RpnCGznjjXu4OI+7jMR8vu7T1ZCCNMzFFAovUCjL1fl0cceksIN1++yQE59SmZw==} - engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + '@inquirer/prompts@8.5.1': + resolution: {integrity: sha512-EwbBgs5e3a5MwQglQ736lx0UY/bbirddCbwe32AJMzQVSeAQK1jmwtDu8lwG9Izp+cdPhZ9NvuEt8aFmav6iLw==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: '@types/node': optional: true - '@inquirer/rawlist@5.2.9': - resolution: {integrity: sha512-a1ErXEfgjfPYpyQ89dp+7n2IISjH9oQg3ygvF5adz8B7aHn4n2PjEgu1wpVTp69K3bj3lVLxP0qJ2b1clk1Whw==} - engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + '@inquirer/rawlist@5.3.1': + resolution: {integrity: sha512-QqdTqQddL3qPX/PPrjobpsO25NZ4dWXgTLenrR445L2ptLEYE6Z+PD5c5CNDJNx4ugRgELAIpSIJxZaO2jJ2Og==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: '@types/node': optional: true - '@inquirer/search@4.1.9': - resolution: {integrity: sha512-ZlbM28Q9lmLkFPNAIv+ZuY530n5Km8U1WW48oYEvDhe9yc2uL3m3t+JSdRUkQlk5fuIuskgiIVjcb7czFzQpuA==} - engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + '@inquirer/search@4.2.1': + resolution: {integrity: sha512-xJj8QWKRSrfKoBIITLZK61dD3zwo0Rz11fgDImku30/Oe81zMdIdGgrLY2h6RkJ+KZ/GhNYIRMKnH/62qBTA5g==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: '@types/node': optional: true - '@inquirer/select@5.1.5': - resolution: {integrity: sha512-6SRg6kHfK/sjLXOsuqNebuir+sjwrf/iWuRUnXgB2slzEewppI1WfzeS16XxDcOQmXBruMmmB9Cgrz7wsAxqMg==} - engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + '@inquirer/select@5.2.1': + resolution: {integrity: sha512-FlDndEUww8m7BfukO2nJa25vhD+H5jxxCv4oGioKqzyWz3nPHhhw4LKdYRSlXuAx7DsdWia7iyaBPKKS95Evfw==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: '@types/node': optional: true - '@inquirer/type@4.0.5': - resolution: {integrity: sha512-aetVUNeKNc/VriqXlw1NRSW0zhMBB0W4bNbWRJgzRl/3d0QNDQFfk0GO5SDdtjMZVg6o8ZKEiadd7SCCzoOn5Q==} - engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + '@inquirer/type@4.0.7': + resolution: {integrity: sha512-t28inv14nMQ1PhKpsJPY+kEs/c00qzeCOS2gTNRyTjG5d6qsVA2fItxW4hkvGZ5lvanGLdtCzVIx5dwdRpN1+g==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: @@ -1021,12 +1043,12 @@ packages: cpu: [x64] os: [win32] - '@noble/ciphers@2.1.1': - resolution: {integrity: sha512-bysYuiVfhxNJuldNXlFEitTVdNnYUc+XNJZd7Qm2a5j1vZHgY+fazadNFWFaMK/2vye0JVlxV3gHmC0WDfAOQw==} + '@noble/ciphers@2.2.0': + resolution: {integrity: sha512-Z6pjIZ/8IJcCGzb2S/0Px5J81yij85xASuk1teLNeg75bfT07MV3a/O2Mtn1I2se43k3lkVEcFaR10N4cgQcZA==} engines: {node: '>= 20.19.0'} - '@noble/hashes@2.0.1': - resolution: {integrity: sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw==} + '@noble/hashes@2.2.0': + resolution: {integrity: sha512-IYqDGiTXab6FniAgnSdZwgWbomxpy9FtYvLKs7wCUs2a8RkITG+DFGO1DM9cr+E3/RgADRpFjrKVaJ1z6sjtEg==} engines: {node: '>= 20.19.0'} '@nodelib/fs.scandir@2.1.5': @@ -1053,8 +1075,8 @@ packages: resolution: {integrity: sha512-/UhIkaZgPutTFmQ7RnIJGgDXZmtEJ7Dvi86xNTFWcnRxVRNk/aotsqDJYeEvDP+FSMB2SdW+pQzNMcWP0rwuNA==} engines: {node: '>=14'} - '@oxc-project/types@0.130.0': - resolution: {integrity: sha512-ibD2usx9JRu7f5pu2tMKMI4cpA4NgXJQoYRP4pQ7Pxmn1l6k/53qWtQWZayhYy3X4QZkt90Ot+mJEaeXouio6Q==} + '@oxc-project/types@0.132.0': + resolution: {integrity: sha512-FESMOxil5Se014ui/Eq8fT5uHJo6nIRwH0PfJrZJXs6Gek3ZVFOrpUv3YIZT20m+extU98Hg1Ym72U58rlsxUQ==} '@playwright/test@1.60.0': resolution: {integrity: sha512-O71yZIbAh/PxDMNGns37GHBIfrVkEVyn+AXyIa5dOTfb4/xNvRWV+Vv/NMbNCtODB/pO7vLlF2OTmMVLhmr7Ag==} @@ -1426,97 +1448,97 @@ packages: '@radix-ui/rect@1.1.1': resolution: {integrity: sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==} - '@rolldown/binding-android-arm64@1.0.1': - resolution: {integrity: sha512-fJI3I0r3C3Oj/zdBCpaCmBRZYf07xpaq4yCfDDoSFm+beWNzbIl26puW8RraUdugoJw/95zerNOn6jasAhzSmg==} + '@rolldown/binding-android-arm64@1.0.2': + resolution: {integrity: sha512-ZS4D1JPGn/MYQN/SYDWftIE/nVsM8j/AFOYEzAoOE2O3NktQOZru+/vYXGbR/qtdLdIfGCP0lcoJiYVzsEz+iQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@rolldown/binding-darwin-arm64@1.0.1': - resolution: {integrity: sha512-cKnAhWEsV7TPcA/5EAteDp6KcJZBQ2G+BqE7zayMMi7kMvwRsbv7WT9aOnn0WNl4SKEIf43vjS31iUPu80nzXg==} + '@rolldown/binding-darwin-arm64@1.0.2': + resolution: {integrity: sha512-vdFA9+C/rekyGce7WqHs/xoT0ioZEWaOFyZLIV1mEeNFaFDUQrPIo8Vs2GvJ6eetb3rzDUtUBgzto3ExpXJB3w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@rolldown/binding-darwin-x64@1.0.1': - resolution: {integrity: sha512-YKrVwQjIRBPo+5G/u03wGjbdy4q7pyzCe93DK9VJ7zkVmeg8LJ7GbgsiHWdR4xSoe4CAXRD7Bcjgbtr64bkXNg==} + '@rolldown/binding-darwin-x64@1.0.2': + resolution: {integrity: sha512-BewSOwTHazv77DTYiAZXSqqKZ4KP/KonFisDMVU7PImxoWfB2aepnPhd2E4SWz3zDzYgDNbs6jBmTdgNnF02GA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@rolldown/binding-freebsd-x64@1.0.1': - resolution: {integrity: sha512-z/oBsREo46SsFqBwYtFe0kpJeBijAT48O/WXLI4suiCLBkr03RTtTJMCzSdDd2znlh8VJizL09XVkQgk8IZonw==} + '@rolldown/binding-freebsd-x64@1.0.2': + resolution: {integrity: sha512-m41o7M0YWtUdqk61Tb+jnKb2rN++iRdIASlExkUoKfIAH30DOHCB8fVLzSUpbWHHU8esmEioY62PxzexE8MBuA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@rolldown/binding-linux-arm-gnueabihf@1.0.1': - resolution: {integrity: sha512-ik8q7GM11zxvYxFc2PeDcT6TBvhCQMaUxfph/M5l9sKuTs/Sjg3L+Byw0F7w0ZVLBZmx30P+gG0ECzzN+MFcmQ==} + '@rolldown/binding-linux-arm-gnueabihf@1.0.2': + resolution: {integrity: sha512-jcojB9H7W/jS29pMKWAK1N+fU99vXodHDTatS3b3y/XSOCiHo0kkA74pL3jJmkoQtYpOCxDvaKs1fo2Ij/1X5w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@rolldown/binding-linux-arm64-gnu@1.0.1': - resolution: {integrity: sha512-QoSx2EkyrrdZ6kcyE8stqZ62t0Yra8Fs5ia9lOxJrh6TMQJK7gQKmscdTHf7pOXKREKrVwOtJcQG3qVSfc866A==} + '@rolldown/binding-linux-arm64-gnu@1.0.2': + resolution: {integrity: sha512-1jn6qDU5iiOgFgygDzKUuKP0maTi0/f1+sBLgvij/76C77Nm3ts6ufz9Bjg5q5dduxiUIxtq86JIoBvo1xQ4Ig==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@rolldown/binding-linux-arm64-musl@1.0.1': - resolution: {integrity: sha512-uwNwFpwKeNiZawfAWBgg0VIztPTV3ihhh1vV334h9ivnNLorxnQMU6Fz8wG1Zb4Qh9LC1/MkcyT3YlDXG3Rsgg==} + '@rolldown/binding-linux-arm64-musl@1.0.2': + resolution: {integrity: sha512-QVLO/czFMdoMFSqlX3bcswcJNm/23r+qoa/jgtmFc/qEp6/jXmIkDjF/XIo8dPfGaiwy1xfQn8o77L79GeXFgw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@rolldown/binding-linux-ppc64-gnu@1.0.1': - resolution: {integrity: sha512-zY1bul7OWr7DFBiJ++wofXvnr8B45ce3QsQUhKrIhXsygAh7bTkwyeM1bi1a2g5C/yC/N8TZyGDEoMfm/l9mpg==} + '@rolldown/binding-linux-ppc64-gnu@1.0.2': + resolution: {integrity: sha512-hgO5Abm0w5UL6FEa2iFnZqo2KlK7TQ5QhV5x09hujBf7t5KzHQ1VmfPuTpqRy/rNlSxua3eWH374xxiVrP+lcA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] - '@rolldown/binding-linux-s390x-gnu@1.0.1': - resolution: {integrity: sha512-0frlsT/f4Ft6I7SMESTKnF3cZsdicQn1dCMkF/jT9wDLE+gGoiQfv1nmT9e+s7s/fekvvy6tZM2jHvI2tkbJDQ==} + '@rolldown/binding-linux-s390x-gnu@1.0.2': + resolution: {integrity: sha512-fy8rXxuYEu602abC8MUNaPjYLIFzReOaEIEMKMUa0rFEUxNpVXhs15KSSQ4qlqSaM7B6rcj9rDZgADh/IGDzLQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] - '@rolldown/binding-linux-x64-gnu@1.0.1': - resolution: {integrity: sha512-XABVmGp9Tg0WspTVvwduTc4fpqy6JnAUrSQe6OuyqD/03nI7r0O9OWUkMIwFrjKAIqolvqoA4ZrJppgwE0Gxmw==} + '@rolldown/binding-linux-x64-gnu@1.0.2': + resolution: {integrity: sha512-0+bOkiQ779+r1WpoHOWHqncvyySci0vKph+myNDYb+im6meJAzHQXay6oEgnkHuUGouM1LKTZwqKpBow6Kj7CQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@rolldown/binding-linux-x64-musl@1.0.1': - resolution: {integrity: sha512-bV4fzswuzVcKD90o/VM6QqKxnxlDq0g2BISDLNVmxrnhpv1DDbyPhCIjYfvzYLV+MvkKKnQt2Q6AO86SEBULUQ==} + '@rolldown/binding-linux-x64-musl@1.0.2': + resolution: {integrity: sha512-mjSkrzZK5Qsl0a9d1JgILOiuZOSDTVdKENcSXBoqbzSrspLR/4/IRVDo5wd2GgZjNss/viBFJdeq+j7qH2nypw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@rolldown/binding-openharmony-arm64@1.0.1': - resolution: {integrity: sha512-/Mh0Zhq3OP7fVs0kcQHZP6lZEthMGTaSf8UBQYSFEZDWGXXlEC+nJ6EqenaK2t4LBXMe3A+K/G2BVXXdtOr4PQ==} + '@rolldown/binding-openharmony-arm64@1.0.2': + resolution: {integrity: sha512-1v5vHasdfQAZoEHakBV72LIFAC9JjnymsiKxp+GEr/ma3+NJCPSaYK+qavInOovJkgwFrs7GccX2d6IgDA3Z5w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@rolldown/binding-wasm32-wasi@1.0.1': - resolution: {integrity: sha512-+1xc9X45l8ufsBAm6Gjvx2qDRIY9lTVt0cgWNcJ+1gdhXvkbxePA60yRTwSTuXL09CMhyJmjpV7E3NoyxbqFQQ==} + '@rolldown/binding-wasm32-wasi@1.0.2': + resolution: {integrity: sha512-mb1VobWn6NheziTk5/WEaR6AKVbrwT5sOi6C7zk3gy/pD1qtJfU1j4PgTo2NJnOtbL9Dl3Aeei8w9jJ7qC2jZQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [wasm32] - '@rolldown/binding-win32-arm64-msvc@1.0.1': - resolution: {integrity: sha512-1D+UqZdfnuR+Jy1GgMJwi85bD40H21uNmOPRWQhw4oRSuolZ/B5rixZ45DK2KXOTCvmVCecauWgEhbw8bI7tOw==} + '@rolldown/binding-win32-arm64-msvc@1.0.2': + resolution: {integrity: sha512-SqKonF56vA/L2yHwHYcEp2P34URpOZ7d1fS635cTkpDnUtEGdUbhI6NzsPdqeSWvAAeGDrxjWjNmibDIdFf9/A==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@rolldown/binding-win32-x64-msvc@1.0.1': - resolution: {integrity: sha512-INAycaWuhlOK3wk4mRHGsdgwYWmd9cChdPdE9bwWmy6rn9VqVNYNFGhOdXrofXUxwHIncSiPNb8tNm8knDVIeQ==} + '@rolldown/binding-win32-x64-msvc@1.0.2': + resolution: {integrity: sha512-v7qRI7gXLRINcOGXt+7YmAZ6iFuyZVMIoXAxhd8oP+DR9dLfL9GfNIx7PLMxmhZdvq8waUJBQiWN9EKNy+TRBQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] @@ -1669,6 +1691,9 @@ packages: '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + '@types/estree@1.0.9': + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} @@ -1678,16 +1703,16 @@ packages: '@types/node@20.19.37': resolution: {integrity: sha512-8kzdPJ3FsNsVIurqBs7oodNnCEVbni9yUEkaHbgptDACOPW04jimGagZ51E6+lXUwJjgnBw+hyko/lkFWCldqw==} - '@types/node@25.8.0': - resolution: {integrity: sha512-TCFSk8IZh+iLX1xtksoBVtdmgL+1IX0fC9BeU4QqFSuNdN/K+HUlhqOzEmSYYpZUVsLYcPqc9KX+60iDuninSQ==} + '@types/node@25.9.1': + resolution: {integrity: sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg==} '@types/react-dom@19.2.3': resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} peerDependencies: '@types/react': ^19.2.0 - '@types/react@19.2.14': - resolution: {integrity: sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==} + '@types/react@19.2.15': + resolution: {integrity: sha512-eRwcGNHve+E8qtEQSSRl6urh+rFop4v8gm6O8rGv25CodbvFdLjA1vVQ1KkiFE0w0UPOnb8tDiFKL5lp0rtY5Q==} '@typescript-eslint/eslint-plugin@8.57.2': resolution: {integrity: sha512-NZZgp0Fm2IkD+La5PR81sd+g+8oS6JwJje+aRWsDocxHkjyRw0J5L5ZTlN3LI1LlOcGL7ph3eaIUmTXMIjLk0w==} @@ -1864,20 +1889,20 @@ packages: babel-plugin-react-compiler: optional: true - '@vitest/coverage-v8@4.1.6': - resolution: {integrity: sha512-36l628fQ/9a/8ihy97eOtEnvWQEdqULQOJtcaxtoNq0G1w3Mxd4szSahOaMM9/NGyZ+hyKcMtIW/WIxq0XQViQ==} + '@vitest/coverage-v8@4.1.7': + resolution: {integrity: sha512-qsYPeXc5Q9dFLd1i8Ap+Bx8sQgcp+rFVQo4R0dDsWNBzl26ldVF1qOO+RL24K7FDrR6pA+50XedRLSoSG24bVQ==} peerDependencies: - '@vitest/browser': 4.1.6 - vitest: 4.1.6 + '@vitest/browser': 4.1.7 + vitest: 4.1.7 peerDependenciesMeta: '@vitest/browser': optional: true - '@vitest/expect@4.1.6': - resolution: {integrity: sha512-7EHDquPthALSV0jhhjgEW8FXaviMx7rSqu8W6oqCoAuOhKov814P99QDV1pxMA3QPv21YudvJngIhjrNI4opLg==} + '@vitest/expect@4.1.7': + resolution: {integrity: sha512-1R+tw0ortHEbZDGMymm+pN7/AFQ/RkFFdtd7EN+VBpynKmLbP8A3rpEXdshBJ7+8hQ9zBJh/i1s0yKNtxAnU7w==} - '@vitest/mocker@4.1.6': - resolution: {integrity: sha512-MCFc63czMjEInOlcY2cpQCvCN+KgbAn+60xu9cMgP4sKaLC5JNAKw7JH8QdAnoAC88hW1IiSNZ+GgVXlN1UcMQ==} + '@vitest/mocker@4.1.7': + resolution: {integrity: sha512-vY7nuamKgfvpA1Koa3oYIw/k7D6kZnpGyNMZW8loow2bsBYla1TFdqTaXncWdRn4pgwNs+90RhnXhJScDwQeJA==} peerDependencies: msw: ^2.4.9 vite: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -1887,20 +1912,20 @@ packages: vite: optional: true - '@vitest/pretty-format@4.1.6': - resolution: {integrity: sha512-h5SxD/IzNhZYnrSZRsUZQIC+vD0GY8cUvq0iwsmkFKixRCKLLWqCXa/FIQ4S1R+sI+PGoojkHsdNrbZiM9Qpgw==} + '@vitest/pretty-format@4.1.7': + resolution: {integrity: sha512-umgCarTOYQWIaDMvGDRZij+6b9oVeLIyJzfN+AS88e0ZOU3QTgNNSTtjQOpcvWr3np1N0j4WgZj+sb3oYBDscw==} - '@vitest/runner@4.1.6': - resolution: {integrity: sha512-nOPCmn2+yD0ZNmKdsXGv/UxMMWbMuKeD6GyYncNwdkYDxpQvrPSKYj2rWuDjC2Y4b6w6hjip5dBKFzEUuZe3vA==} + '@vitest/runner@4.1.7': + resolution: {integrity: sha512-BapjmAQ2aI78WdMEfeUWivnfVzB+VPGwWRQcJE0OUq7qEeEcBsCSf+0T5iREBNE5nBb4wA5Ya0W6IA+sghdEFw==} - '@vitest/snapshot@4.1.6': - resolution: {integrity: sha512-YhsdE6xAVfTDmzjxL2ZDUvjj+ZsgyOKe+TdQzqkD72wIOmHka8NuGQ6NpTNZv9D2Z63fbwWKJPeVpEw4EQgYxw==} + '@vitest/snapshot@4.1.7': + resolution: {integrity: sha512-ZacLzja+TmJeZ1h14xW2FB/WpeimUD3haBXQPyJqxvo8jQTmfeA8zv58mtjN2C7EHXZDYVcVYdYmAxjkWVvKCw==} - '@vitest/spy@4.1.6': - resolution: {integrity: sha512-JFKxMx6udhwKh/Ldo270e17QX710vgunMkuPAvXjHSvC6oqLWAHhVhjg/I71q0u0CBSErIODV1Kjv0FQNSWjdg==} + '@vitest/spy@4.1.7': + resolution: {integrity: sha512-kbkI5LMWakyuTIvs6fUJ5qdIVb1XVKsYJAT4OJ938cHMROYMSfmoQdZy0aaAnjbbc8F61vkoTqz/Az+/HiIu5Q==} - '@vitest/utils@4.1.6': - resolution: {integrity: sha512-FxIY+U81R3LGKCxaHHFRQ5+g6/iRgGLmeHWdp2Amj4ljQRrEIWHmZyDfDYBRZlpyqA7qKxtS9DD1dhk8RnRIVQ==} + '@vitest/utils@4.1.7': + resolution: {integrity: sha512-T532WBu791cBxJlCl6SO+J14l81DQx6uQHm1bQbmCDY7nqlEIgkza/UFnSBNaUtSf41unldDFjdOBYEQC4b5Hw==} acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} @@ -1980,8 +2005,8 @@ packages: ast-types-flow@0.0.8: resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==} - ast-v8-to-istanbul@1.0.0: - resolution: {integrity: sha512-1fSfIwuDICFA4LKkCzRPO7F0hzFf0B7+Xqrl27ynQaa+Rh0e1Es0v6kWHPott3lU10AyAr7oKHa65OppjLn3Rg==} + ast-v8-to-istanbul@1.0.2: + resolution: {integrity: sha512-dKmJxJsGItLmc5CYZKuEjuG6GnBs6PG4gohMhyFOWKaNQoYCuRZJDECaBlHmcG0lv2wc2E0uU8lESmBEumC3DQ==} async-function@1.0.0: resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} @@ -1992,7 +2017,7 @@ packages: engines: {node: ^10 || ^12 || >=14} hasBin: true peerDependencies: - postcss: ^8.1.0 + postcss: '>=8.5.10' available-typed-arrays@1.0.7: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} @@ -2023,8 +2048,8 @@ packages: engines: {node: '>=6.0.0'} hasBin: true - better-auth@1.6.11: - resolution: {integrity: sha512-Wwt6+q07dwIhsp6XiM7L1qSXVUWBEtNl+eZvwM778CguFqDZFBN9Pt6LtFaHl55t8Z+Zc//5kxcbgDY8/79vFQ==} + better-auth@1.6.12: + resolution: {integrity: sha512-vJG8hB+zcayZEJgcWGTzP2XODZuf/WKViOtam+uhhQ9879yc7fDWAV9O4jSs+R28noSXIAaB3zhIMN3DaDO3cA==} peerDependencies: '@lynx-js/react': '*' '@prisma/client': ^5.0.0 || ^6.0.0 || ^7.0.0 @@ -2096,8 +2121,8 @@ packages: bidi-js@1.0.3: resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==} - brace-expansion@1.1.12: - resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} + brace-expansion@1.1.15: + resolution: {integrity: sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==} brace-expansion@5.0.6: resolution: {integrity: sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==} @@ -2500,8 +2525,8 @@ packages: fast-string-width@3.0.2: resolution: {integrity: sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==} - fast-wrap-ansi@0.2.0: - resolution: {integrity: sha512-rLV8JHxTyhVmFYhBJuMujcrHqOT2cnO5Zxj37qROj23CP39GXubJRBUFF0z8KFK77Uc0SukZUf7JZhsVEQ6n8w==} + fast-wrap-ansi@0.2.2: + resolution: {integrity: sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==} fastq@1.20.1: resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} @@ -2982,8 +3007,8 @@ packages: lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} - lucide-react@1.16.0: - resolution: {integrity: sha512-dYwyPzb4MEKpGUmNYk3WKWPnMrHs3FKM+q94kAnJrcDIqqn1hq2xY8scaS2ovsOCM5D51ey2gaRG3PBb1vgoYQ==} + lucide-react@1.17.0: + resolution: {integrity: sha512-9FA9evdox/JQL5PT57fdA1x/yg8T7knJ98+zjTL3UfKza6pflQUUh3XtaQIHKvnsJw1lmsEyHVlt5jchYxOQ5w==} peerDependencies: react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 @@ -3037,13 +3062,13 @@ packages: resolution: {integrity: sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw==} engines: {node: ^20.17.0 || >=22.9.0} - nanoid@3.3.11: - resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + nanoid@3.3.12: + resolution: {integrity: sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - nanostores@1.2.0: - resolution: {integrity: sha512-F0wCzbsH80G7XXo0Jd9/AVQC7ouWY6idUCTnMwW5t/Rv9W8qmO6endavDwg7TNp5GbugwSukFMVZqzPSrSMndg==} + nanostores@1.3.0: + resolution: {integrity: sha512-XPUa/jz+P1oJvN9VBxw4L9MtdFfaH3DAryqPssqhb2kXjmb9npz0dly6rCsgFWOPr4Yg9mTfM3MDZgZZ+7A3lA==} engines: {node: ^20.0.0 || >=22.0.0} napi-postinstall@0.3.4: @@ -3185,12 +3210,8 @@ packages: postcss-value-parser@4.2.0: resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} - postcss@8.4.31: - resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} - engines: {node: ^10 || ^12 || >=14} - - postcss@8.5.14: - resolution: {integrity: sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==} + postcss@8.5.15: + resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==} engines: {node: ^10 || ^12 || >=14} prelude-ls@1.2.1: @@ -3216,8 +3237,8 @@ packages: peerDependencies: react: ^19.2.6 - react-hook-form@7.76.0: - resolution: {integrity: sha512-eKtLGgFeSgkHqQD8J59AMZ9a4uD1D83iSIzt4YlTGD7liDen5rrjcUO1rVIGd9yC1gofryjtHbv+4ny4hkLWlw==} + react-hook-form@7.76.1: + resolution: {integrity: sha512-rYM7tPiWlu3nZchkR/ex7piyzui2vFPyaLnXnI/RnblB/L4qfMmyses8llJVtF1NpE9WBBsJlGtcSZzPCXW1qQ==} engines: {node: '>=18.0.0'} peerDependencies: react: ^16.8.0 || ^17 || ^18 || ^19 @@ -3303,8 +3324,8 @@ packages: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - rolldown@1.0.1: - resolution: {integrity: sha512-X0KQHljNnEkWNqqiz9zJrGunh1B0HgOxLXvnFpCOcadzcy5qohZ3tqMEUg00vncoRovXuK3ZqCT9KnnKzoInFQ==} + rolldown@1.0.2: + resolution: {integrity: sha512-oZx5zVDtVB44AW3eaifgDml1gWRDZGvjcfdxonE4swNPG98PrrXjaO/KrnUjzlMnztCCRVlUueA1kCXhARGk6g==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true @@ -3348,6 +3369,11 @@ packages: engines: {node: '>=10'} hasBin: true + semver@7.8.1: + resolution: {integrity: sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==} + engines: {node: '>=10'} + hasBin: true + set-cookie-parser@3.1.0: resolution: {integrity: sha512-kjnC1DXBHcxaOaOXBHBeRtltsDG2nUiUni+jP92M9gYdW12rsmx92UsfpH7o5tDRs7I1ZZPSQJQGv3UaRfCiuw==} @@ -3503,16 +3529,16 @@ packages: tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} - tinyexec@1.1.2: - resolution: {integrity: sha512-dAqSqE/RabpBKI8+h26GfLq6Vb3JVXs30XYQjdMjaj/c2tS8IYYMbIzP599KtRj7c57/wYApb3QjgRgXmrCukA==} + tinyexec@1.2.3: + resolution: {integrity: sha512-g62dB+w1/OEFnPvmX0yd/HnetYITOL+1nJW7kitOycOeAvmbWC/nu0fwmmQ/kupNojqExzyC/T++pST/jRJ2mQ==} engines: {node: '>=18'} tinyglobby@0.2.15: resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} engines: {node: '>=12.0.0'} - tinyglobby@0.2.16: - resolution: {integrity: sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==} + tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} engines: {node: '>=12.0.0'} tinyrainbow@3.1.0: @@ -3554,8 +3580,8 @@ packages: tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - tsx@4.22.1: - resolution: {integrity: sha512-TvncJykhxAzFCk0VQZKBTClall4Pm7qXDSodb6uxi8QFa8X8mT6ABjxxsQ2opDRYxG7AzcRWXaFtruz5HJKuWg==} + tsx@4.22.3: + resolution: {integrity: sha512-mdoNxBC/cSQObGGVQ5Bpn5i+yv7j68gk3Nfm3wFjcJg3Z0Mix9jzAFfP12prmm5eVGmDKtp0yyArrs0Q+8gZHg==} engines: {node: '>=18.0.0'} hasBin: true @@ -3645,8 +3671,8 @@ packages: '@types/react': optional: true - vite@8.0.13: - resolution: {integrity: sha512-MFtjBYgzmSxmgA4RAfjIyXWpGe1oALnjgUTzzV7QLx/TKxCzjtMH6Fd9/eVK+5Fg1qNoz5VAwsmMs/NofrmJvw==} + vite@8.0.14: + resolution: {integrity: sha512-s4BJJ+5y1pYL6Otw51FHhVJQhPnuRinKig64g/1+EUNaJsd3gCKdD31IPFvswUgW9/60QT9oFHbZHbQK5imcxw==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -3688,20 +3714,20 @@ packages: yaml: optional: true - vitest@4.1.6: - resolution: {integrity: sha512-6lvjbS3p9b4CrdCmguzbh2/4uoXhGE2q71R4OX5sqF9R1bo9Xd6fGrMAfvp5wnCzlBnFVdCOp6onuTQVbo8iUQ==} + vitest@4.1.7: + resolution: {integrity: sha512-flYyaFd2CgoCoU+0UKt3pxksgC+S02iTDN0n3LtqaMeXsI9SBcdNujc2k0DeFLzUn/0k538yNjOSdwgCqcrwJA==} engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@opentelemetry/api': ^1.9.0 '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 - '@vitest/browser-playwright': 4.1.6 - '@vitest/browser-preview': 4.1.6 - '@vitest/browser-webdriverio': 4.1.6 - '@vitest/coverage-istanbul': 4.1.6 - '@vitest/coverage-v8': 4.1.6 - '@vitest/ui': 4.1.6 + '@vitest/browser-playwright': 4.1.7 + '@vitest/browser-preview': 4.1.7 + '@vitest/browser-webdriverio': 4.1.7 + '@vitest/coverage-istanbul': 4.1.7 + '@vitest/coverage-v8': 4.1.7 + '@vitest/ui': 4.1.7 happy-dom: '*' jsdom: '*' vite: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -3811,9 +3837,6 @@ packages: peerDependencies: zod: ^3.25.0 || ^4.0.0 - zod@4.3.6: - resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==} - zod@4.4.3: resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} @@ -3849,6 +3872,12 @@ snapshots: js-tokens: 4.0.0 picocolors: 1.1.1 + '@babel/code-frame@7.29.7': + dependencies: + '@babel/helper-validator-identifier': 7.29.7 + js-tokens: 4.0.0 + picocolors: 1.1.1 + '@babel/compat-data@7.29.0': {} '@babel/core@7.29.0': @@ -3907,8 +3936,12 @@ snapshots: '@babel/helper-string-parser@7.27.1': {} + '@babel/helper-string-parser@7.29.7': {} + '@babel/helper-validator-identifier@7.28.5': {} + '@babel/helper-validator-identifier@7.29.7': {} + '@babel/helper-validator-option@7.27.1': {} '@babel/helpers@7.29.2': @@ -3920,12 +3953,14 @@ snapshots: dependencies: '@babel/types': 7.29.0 - '@babel/parser@7.29.3': + '@babel/parser@7.29.7': dependencies: - '@babel/types': 7.29.0 + '@babel/types': 7.29.7 '@babel/runtime@7.29.2': {} + '@babel/runtime@7.29.7': {} + '@babel/template@7.28.6': dependencies: '@babel/code-frame': 7.29.0 @@ -3949,58 +3984,63 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 + '@babel/types@7.29.7': + dependencies: + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + '@bcoe/v8-coverage@1.0.2': {} - '@better-auth/core@1.6.11(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.2.0)': + '@better-auth/core@1.6.12(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.3.0)': dependencies: - '@better-auth/utils': 0.4.0 + '@better-auth/utils': 0.4.1 '@better-fetch/fetch': 1.1.21 '@opentelemetry/semantic-conventions': 1.41.1 '@standard-schema/spec': 1.1.0 better-call: 1.3.5(zod@4.4.3) jose: 6.2.3 kysely: 0.28.17 - nanostores: 1.2.0 + nanostores: 1.3.0 zod: 4.4.3 optionalDependencies: '@opentelemetry/api': 1.9.0 - '@better-auth/drizzle-adapter@1.6.11(@better-auth/core@1.6.11(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.2.0))(@better-auth/utils@0.4.0)': + '@better-auth/drizzle-adapter@1.6.12(@better-auth/core@1.6.12(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.3.0))(@better-auth/utils@0.4.1)': dependencies: - '@better-auth/core': 1.6.11(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.2.0) - '@better-auth/utils': 0.4.0 + '@better-auth/core': 1.6.12(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.3.0) + '@better-auth/utils': 0.4.1 - '@better-auth/kysely-adapter@1.6.11(@better-auth/core@1.6.11(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.2.0))(@better-auth/utils@0.4.0)(kysely@0.28.17)': + '@better-auth/kysely-adapter@1.6.12(@better-auth/core@1.6.12(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.3.0))(@better-auth/utils@0.4.1)(kysely@0.28.17)': dependencies: - '@better-auth/core': 1.6.11(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.2.0) - '@better-auth/utils': 0.4.0 + '@better-auth/core': 1.6.12(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.3.0) + '@better-auth/utils': 0.4.1 optionalDependencies: kysely: 0.28.17 - '@better-auth/memory-adapter@1.6.11(@better-auth/core@1.6.11(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.2.0))(@better-auth/utils@0.4.0)': + '@better-auth/memory-adapter@1.6.12(@better-auth/core@1.6.12(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.3.0))(@better-auth/utils@0.4.1)': dependencies: - '@better-auth/core': 1.6.11(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.2.0) - '@better-auth/utils': 0.4.0 + '@better-auth/core': 1.6.12(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.3.0) + '@better-auth/utils': 0.4.1 - '@better-auth/mongo-adapter@1.6.11(@better-auth/core@1.6.11(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.2.0))(@better-auth/utils@0.4.0)': + '@better-auth/mongo-adapter@1.6.12(@better-auth/core@1.6.12(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.3.0))(@better-auth/utils@0.4.1)': dependencies: - '@better-auth/core': 1.6.11(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.2.0) - '@better-auth/utils': 0.4.0 + '@better-auth/core': 1.6.12(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.3.0) + '@better-auth/utils': 0.4.1 - '@better-auth/prisma-adapter@1.6.11(@better-auth/core@1.6.11(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.2.0))(@better-auth/utils@0.4.0)': + '@better-auth/prisma-adapter@1.6.12(@better-auth/core@1.6.12(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.3.0))(@better-auth/utils@0.4.1)': dependencies: - '@better-auth/core': 1.6.11(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.2.0) - '@better-auth/utils': 0.4.0 + '@better-auth/core': 1.6.12(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.3.0) + '@better-auth/utils': 0.4.1 - '@better-auth/telemetry@1.6.11(@better-auth/core@1.6.11(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.2.0))(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)': + '@better-auth/telemetry@1.6.12(@better-auth/core@1.6.12(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.3.0))(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.1.21)': dependencies: - '@better-auth/core': 1.6.11(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.2.0) - '@better-auth/utils': 0.4.0 + '@better-auth/core': 1.6.12(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.3.0) + '@better-auth/utils': 0.4.1 '@better-fetch/fetch': 1.1.21 - '@better-auth/utils@0.4.0': + '@better-auth/utils@0.4.1': dependencies: - '@noble/hashes': 2.0.1 + '@noble/hashes': 2.2.0 '@better-fetch/fetch@1.1.21': {} @@ -4188,9 +4228,9 @@ snapshots: '@eslint/core': 0.17.0 levn: 0.4.1 - '@exodus/bytes@1.15.0(@noble/hashes@2.0.1)': + '@exodus/bytes@1.15.0(@noble/hashes@2.2.0)': optionalDependencies: - '@noble/hashes': 2.0.1 + '@noble/hashes': 2.2.0 '@floating-ui/core@1.7.5': dependencies: @@ -4213,10 +4253,10 @@ snapshots: dependencies: react: 19.2.6 - '@hookform/resolvers@5.2.2(react-hook-form@7.76.0(react@19.2.6))': + '@hookform/resolvers@5.4.0(react-hook-form@7.76.1(react@19.2.6))': dependencies: '@standard-schema/utils': 0.3.0 - react-hook-form: 7.76.0(react@19.2.6) + react-hook-form: 7.76.1(react@19.2.6) '@humanfs/core@0.19.1': {} @@ -4326,124 +4366,124 @@ snapshots: '@img/sharp-win32-x64@0.34.5': optional: true - '@inquirer/ansi@2.0.5': {} + '@inquirer/ansi@2.0.7': {} - '@inquirer/checkbox@5.1.5(@types/node@25.8.0)': + '@inquirer/checkbox@5.2.1(@types/node@25.9.1)': dependencies: - '@inquirer/ansi': 2.0.5 - '@inquirer/core': 11.1.10(@types/node@25.8.0) - '@inquirer/figures': 2.0.5 - '@inquirer/type': 4.0.5(@types/node@25.8.0) + '@inquirer/ansi': 2.0.7 + '@inquirer/core': 11.2.1(@types/node@25.9.1) + '@inquirer/figures': 2.0.7 + '@inquirer/type': 4.0.7(@types/node@25.9.1) optionalDependencies: - '@types/node': 25.8.0 + '@types/node': 25.9.1 - '@inquirer/confirm@6.0.13(@types/node@25.8.0)': + '@inquirer/confirm@6.1.1(@types/node@25.9.1)': dependencies: - '@inquirer/core': 11.1.10(@types/node@25.8.0) - '@inquirer/type': 4.0.5(@types/node@25.8.0) + '@inquirer/core': 11.2.1(@types/node@25.9.1) + '@inquirer/type': 4.0.7(@types/node@25.9.1) optionalDependencies: - '@types/node': 25.8.0 + '@types/node': 25.9.1 - '@inquirer/core@11.1.10(@types/node@25.8.0)': + '@inquirer/core@11.2.1(@types/node@25.9.1)': dependencies: - '@inquirer/ansi': 2.0.5 - '@inquirer/figures': 2.0.5 - '@inquirer/type': 4.0.5(@types/node@25.8.0) + '@inquirer/ansi': 2.0.7 + '@inquirer/figures': 2.0.7 + '@inquirer/type': 4.0.7(@types/node@25.9.1) cli-width: 4.1.0 - fast-wrap-ansi: 0.2.0 + fast-wrap-ansi: 0.2.2 mute-stream: 3.0.0 signal-exit: 4.1.0 optionalDependencies: - '@types/node': 25.8.0 + '@types/node': 25.9.1 - '@inquirer/editor@5.1.2(@types/node@25.8.0)': + '@inquirer/editor@5.2.1(@types/node@25.9.1)': dependencies: - '@inquirer/core': 11.1.10(@types/node@25.8.0) - '@inquirer/external-editor': 3.0.0(@types/node@25.8.0) - '@inquirer/type': 4.0.5(@types/node@25.8.0) + '@inquirer/core': 11.2.1(@types/node@25.9.1) + '@inquirer/external-editor': 3.0.2(@types/node@25.9.1) + '@inquirer/type': 4.0.7(@types/node@25.9.1) optionalDependencies: - '@types/node': 25.8.0 + '@types/node': 25.9.1 - '@inquirer/expand@5.0.14(@types/node@25.8.0)': + '@inquirer/expand@5.1.1(@types/node@25.9.1)': dependencies: - '@inquirer/core': 11.1.10(@types/node@25.8.0) - '@inquirer/type': 4.0.5(@types/node@25.8.0) + '@inquirer/core': 11.2.1(@types/node@25.9.1) + '@inquirer/type': 4.0.7(@types/node@25.9.1) optionalDependencies: - '@types/node': 25.8.0 + '@types/node': 25.9.1 - '@inquirer/external-editor@3.0.0(@types/node@25.8.0)': + '@inquirer/external-editor@3.0.2(@types/node@25.9.1)': dependencies: chardet: 2.1.1 iconv-lite: 0.7.2 optionalDependencies: - '@types/node': 25.8.0 + '@types/node': 25.9.1 - '@inquirer/figures@2.0.5': {} + '@inquirer/figures@2.0.7': {} - '@inquirer/input@5.0.13(@types/node@25.8.0)': + '@inquirer/input@5.1.1(@types/node@25.9.1)': dependencies: - '@inquirer/core': 11.1.10(@types/node@25.8.0) - '@inquirer/type': 4.0.5(@types/node@25.8.0) + '@inquirer/core': 11.2.1(@types/node@25.9.1) + '@inquirer/type': 4.0.7(@types/node@25.9.1) optionalDependencies: - '@types/node': 25.8.0 + '@types/node': 25.9.1 - '@inquirer/number@4.0.13(@types/node@25.8.0)': + '@inquirer/number@4.1.1(@types/node@25.9.1)': dependencies: - '@inquirer/core': 11.1.10(@types/node@25.8.0) - '@inquirer/type': 4.0.5(@types/node@25.8.0) + '@inquirer/core': 11.2.1(@types/node@25.9.1) + '@inquirer/type': 4.0.7(@types/node@25.9.1) optionalDependencies: - '@types/node': 25.8.0 + '@types/node': 25.9.1 - '@inquirer/password@5.0.13(@types/node@25.8.0)': + '@inquirer/password@5.1.1(@types/node@25.9.1)': dependencies: - '@inquirer/ansi': 2.0.5 - '@inquirer/core': 11.1.10(@types/node@25.8.0) - '@inquirer/type': 4.0.5(@types/node@25.8.0) + '@inquirer/ansi': 2.0.7 + '@inquirer/core': 11.2.1(@types/node@25.9.1) + '@inquirer/type': 4.0.7(@types/node@25.9.1) optionalDependencies: - '@types/node': 25.8.0 - - '@inquirer/prompts@8.4.3(@types/node@25.8.0)': - dependencies: - '@inquirer/checkbox': 5.1.5(@types/node@25.8.0) - '@inquirer/confirm': 6.0.13(@types/node@25.8.0) - '@inquirer/editor': 5.1.2(@types/node@25.8.0) - '@inquirer/expand': 5.0.14(@types/node@25.8.0) - '@inquirer/input': 5.0.13(@types/node@25.8.0) - '@inquirer/number': 4.0.13(@types/node@25.8.0) - '@inquirer/password': 5.0.13(@types/node@25.8.0) - '@inquirer/rawlist': 5.2.9(@types/node@25.8.0) - '@inquirer/search': 4.1.9(@types/node@25.8.0) - '@inquirer/select': 5.1.5(@types/node@25.8.0) + '@types/node': 25.9.1 + + '@inquirer/prompts@8.5.1(@types/node@25.9.1)': + dependencies: + '@inquirer/checkbox': 5.2.1(@types/node@25.9.1) + '@inquirer/confirm': 6.1.1(@types/node@25.9.1) + '@inquirer/editor': 5.2.1(@types/node@25.9.1) + '@inquirer/expand': 5.1.1(@types/node@25.9.1) + '@inquirer/input': 5.1.1(@types/node@25.9.1) + '@inquirer/number': 4.1.1(@types/node@25.9.1) + '@inquirer/password': 5.1.1(@types/node@25.9.1) + '@inquirer/rawlist': 5.3.1(@types/node@25.9.1) + '@inquirer/search': 4.2.1(@types/node@25.9.1) + '@inquirer/select': 5.2.1(@types/node@25.9.1) optionalDependencies: - '@types/node': 25.8.0 + '@types/node': 25.9.1 - '@inquirer/rawlist@5.2.9(@types/node@25.8.0)': + '@inquirer/rawlist@5.3.1(@types/node@25.9.1)': dependencies: - '@inquirer/core': 11.1.10(@types/node@25.8.0) - '@inquirer/type': 4.0.5(@types/node@25.8.0) + '@inquirer/core': 11.2.1(@types/node@25.9.1) + '@inquirer/type': 4.0.7(@types/node@25.9.1) optionalDependencies: - '@types/node': 25.8.0 + '@types/node': 25.9.1 - '@inquirer/search@4.1.9(@types/node@25.8.0)': + '@inquirer/search@4.2.1(@types/node@25.9.1)': dependencies: - '@inquirer/core': 11.1.10(@types/node@25.8.0) - '@inquirer/figures': 2.0.5 - '@inquirer/type': 4.0.5(@types/node@25.8.0) + '@inquirer/core': 11.2.1(@types/node@25.9.1) + '@inquirer/figures': 2.0.7 + '@inquirer/type': 4.0.7(@types/node@25.9.1) optionalDependencies: - '@types/node': 25.8.0 + '@types/node': 25.9.1 - '@inquirer/select@5.1.5(@types/node@25.8.0)': + '@inquirer/select@5.2.1(@types/node@25.9.1)': dependencies: - '@inquirer/ansi': 2.0.5 - '@inquirer/core': 11.1.10(@types/node@25.8.0) - '@inquirer/figures': 2.0.5 - '@inquirer/type': 4.0.5(@types/node@25.8.0) + '@inquirer/ansi': 2.0.7 + '@inquirer/core': 11.2.1(@types/node@25.9.1) + '@inquirer/figures': 2.0.7 + '@inquirer/type': 4.0.7(@types/node@25.9.1) optionalDependencies: - '@types/node': 25.8.0 + '@types/node': 25.9.1 - '@inquirer/type@4.0.5(@types/node@25.8.0)': + '@inquirer/type@4.0.7(@types/node@25.9.1)': optionalDependencies: - '@types/node': 25.8.0 + '@types/node': 25.9.1 '@jridgewell/gen-mapping@0.3.13': dependencies: @@ -4508,9 +4548,9 @@ snapshots: '@next/swc-win32-x64-msvc@16.2.6': optional: true - '@noble/ciphers@2.1.1': {} + '@noble/ciphers@2.2.0': {} - '@noble/hashes@2.0.1': {} + '@noble/hashes@2.2.0': {} '@nodelib/fs.scandir@2.1.5': dependencies: @@ -4531,7 +4571,7 @@ snapshots: '@opentelemetry/semantic-conventions@1.41.1': {} - '@oxc-project/types@0.130.0': {} + '@oxc-project/types@0.132.0': {} '@playwright/test@1.60.0': dependencies: @@ -4541,398 +4581,398 @@ snapshots: '@radix-ui/primitive@1.1.3': {} - '@radix-ui/react-arrow@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@radix-ui/react-arrow@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) react: 19.2.6 react-dom: 19.2.6(react@19.2.6) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@types/react': 19.2.15 + '@types/react-dom': 19.2.3(@types/react@19.2.15) - '@radix-ui/react-collection@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@radix-ui/react-collection@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.6) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.6) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.14)(react@19.2.6) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.15)(react@19.2.6) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.15)(react@19.2.6) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.15)(react@19.2.6) react: 19.2.6 react-dom: 19.2.6(react@19.2.6) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@types/react': 19.2.15 + '@types/react-dom': 19.2.3(@types/react@19.2.15) - '@radix-ui/react-compose-refs@1.1.2(@types/react@19.2.14)(react@19.2.6)': + '@radix-ui/react-compose-refs@1.1.2(@types/react@19.2.15)(react@19.2.6)': dependencies: react: 19.2.6 optionalDependencies: - '@types/react': 19.2.14 + '@types/react': 19.2.15 - '@radix-ui/react-context@1.1.2(@types/react@19.2.14)(react@19.2.6)': + '@radix-ui/react-context@1.1.2(@types/react@19.2.15)(react@19.2.6)': dependencies: react: 19.2.6 optionalDependencies: - '@types/react': 19.2.14 + '@types/react': 19.2.15 - '@radix-ui/react-dialog@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@radix-ui/react-dialog@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.6) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.6) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.14)(react@19.2.6) - '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.14)(react@19.2.6) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.14)(react@19.2.6) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.14)(react@19.2.6) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.15)(react@19.2.6) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.15)(react@19.2.6) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.15)(react@19.2.6) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.15)(react@19.2.6) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.15)(react@19.2.6) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.15)(react@19.2.6) aria-hidden: 1.2.6 react: 19.2.6 react-dom: 19.2.6(react@19.2.6) - react-remove-scroll: 2.7.2(@types/react@19.2.14)(react@19.2.6) + react-remove-scroll: 2.7.2(@types/react@19.2.15)(react@19.2.6) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@types/react': 19.2.15 + '@types/react-dom': 19.2.3(@types/react@19.2.15) - '@radix-ui/react-direction@1.1.1(@types/react@19.2.14)(react@19.2.6)': + '@radix-ui/react-direction@1.1.1(@types/react@19.2.15)(react@19.2.6)': dependencies: react: 19.2.6 optionalDependencies: - '@types/react': 19.2.14 + '@types/react': 19.2.15 - '@radix-ui/react-dismissable-layer@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@radix-ui/react-dismissable-layer@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.6) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.14)(react@19.2.6) - '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.2.14)(react@19.2.6) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.15)(react@19.2.6) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.15)(react@19.2.6) + '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.2.15)(react@19.2.6) react: 19.2.6 react-dom: 19.2.6(react@19.2.6) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@types/react': 19.2.15 + '@types/react-dom': 19.2.3(@types/react@19.2.15) - '@radix-ui/react-dropdown-menu@2.1.16(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@radix-ui/react-dropdown-menu@2.1.16(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.6) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.6) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.14)(react@19.2.6) - '@radix-ui/react-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.14)(react@19.2.6) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.15)(react@19.2.6) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.15)(react@19.2.6) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.15)(react@19.2.6) + '@radix-ui/react-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.15)(react@19.2.6) react: 19.2.6 react-dom: 19.2.6(react@19.2.6) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@types/react': 19.2.15 + '@types/react-dom': 19.2.3(@types/react@19.2.15) - '@radix-ui/react-focus-guards@1.1.3(@types/react@19.2.14)(react@19.2.6)': + '@radix-ui/react-focus-guards@1.1.3(@types/react@19.2.15)(react@19.2.6)': dependencies: react: 19.2.6 optionalDependencies: - '@types/react': 19.2.14 + '@types/react': 19.2.15 - '@radix-ui/react-focus-scope@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@radix-ui/react-focus-scope@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.6) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.14)(react@19.2.6) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.15)(react@19.2.6) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.15)(react@19.2.6) react: 19.2.6 react-dom: 19.2.6(react@19.2.6) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@types/react': 19.2.15 + '@types/react-dom': 19.2.3(@types/react@19.2.15) - '@radix-ui/react-id@1.1.1(@types/react@19.2.14)(react@19.2.6)': + '@radix-ui/react-id@1.1.1(@types/react@19.2.15)(react@19.2.6)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.6) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.15)(react@19.2.6) react: 19.2.6 optionalDependencies: - '@types/react': 19.2.14 + '@types/react': 19.2.15 - '@radix-ui/react-label@2.1.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@radix-ui/react-label@2.1.8(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: - '@radix-ui/react-primitive': 2.1.4(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-primitive': 2.1.4(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) react: 19.2.6 react-dom: 19.2.6(react@19.2.6) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@types/react': 19.2.15 + '@types/react-dom': 19.2.3(@types/react@19.2.15) - '@radix-ui/react-menu@2.1.16(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@radix-ui/react-menu@2.1.16(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.6) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.6) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.14)(react@19.2.6) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.14)(react@19.2.6) - '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.14)(react@19.2.6) - '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.14)(react@19.2.6) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.14)(react@19.2.6) + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.15)(react@19.2.6) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.15)(react@19.2.6) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.15)(react@19.2.6) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.15)(react@19.2.6) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.15)(react@19.2.6) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.15)(react@19.2.6) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.15)(react@19.2.6) aria-hidden: 1.2.6 react: 19.2.6 react-dom: 19.2.6(react@19.2.6) - react-remove-scroll: 2.7.2(@types/react@19.2.14)(react@19.2.6) + react-remove-scroll: 2.7.2(@types/react@19.2.15)(react@19.2.6) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@types/react': 19.2.15 + '@types/react-dom': 19.2.3(@types/react@19.2.15) - '@radix-ui/react-popper@1.2.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@radix-ui/react-popper@1.2.8(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: '@floating-ui/react-dom': 2.1.8(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-arrow': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.6) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.6) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.14)(react@19.2.6) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.6) - '@radix-ui/react-use-rect': 1.1.1(@types/react@19.2.14)(react@19.2.6) - '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.14)(react@19.2.6) + '@radix-ui/react-arrow': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.15)(react@19.2.6) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.15)(react@19.2.6) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.15)(react@19.2.6) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.15)(react@19.2.6) + '@radix-ui/react-use-rect': 1.1.1(@types/react@19.2.15)(react@19.2.6) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.15)(react@19.2.6) '@radix-ui/rect': 1.1.1 react: 19.2.6 react-dom: 19.2.6(react@19.2.6) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@types/react': 19.2.15 + '@types/react-dom': 19.2.3(@types/react@19.2.15) - '@radix-ui/react-portal@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@radix-ui/react-portal@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.6) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.15)(react@19.2.6) react: 19.2.6 react-dom: 19.2.6(react@19.2.6) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@types/react': 19.2.15 + '@types/react-dom': 19.2.3(@types/react@19.2.15) - '@radix-ui/react-presence@1.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@radix-ui/react-presence@1.1.5(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.6) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.6) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.15)(react@19.2.6) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.15)(react@19.2.6) react: 19.2.6 react-dom: 19.2.6(react@19.2.6) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@types/react': 19.2.15 + '@types/react-dom': 19.2.3(@types/react@19.2.15) - '@radix-ui/react-primitive@2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@radix-ui/react-primitive@2.1.3(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.14)(react@19.2.6) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.15)(react@19.2.6) react: 19.2.6 react-dom: 19.2.6(react@19.2.6) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@types/react': 19.2.15 + '@types/react-dom': 19.2.3(@types/react@19.2.15) - '@radix-ui/react-primitive@2.1.4(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@radix-ui/react-primitive@2.1.4(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: - '@radix-ui/react-slot': 1.2.4(@types/react@19.2.14)(react@19.2.6) + '@radix-ui/react-slot': 1.2.4(@types/react@19.2.15)(react@19.2.6) react: 19.2.6 react-dom: 19.2.6(react@19.2.6) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@types/react': 19.2.15 + '@types/react-dom': 19.2.3(@types/react@19.2.15) - '@radix-ui/react-roving-focus@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@radix-ui/react-roving-focus@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.6) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.6) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.14)(react@19.2.6) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.14)(react@19.2.6) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.14)(react@19.2.6) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.14)(react@19.2.6) + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.15)(react@19.2.6) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.15)(react@19.2.6) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.15)(react@19.2.6) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.15)(react@19.2.6) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.15)(react@19.2.6) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.15)(react@19.2.6) react: 19.2.6 react-dom: 19.2.6(react@19.2.6) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@types/react': 19.2.15 + '@types/react-dom': 19.2.3(@types/react@19.2.15) - '@radix-ui/react-select@2.2.6(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@radix-ui/react-select@2.2.6(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: '@radix-ui/number': 1.1.1 '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.6) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.6) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.14)(react@19.2.6) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.14)(react@19.2.6) - '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.14)(react@19.2.6) - '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.14)(react@19.2.6) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.14)(react@19.2.6) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.14)(react@19.2.6) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.6) - '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.14)(react@19.2.6) - '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.15)(react@19.2.6) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.15)(react@19.2.6) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.15)(react@19.2.6) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.15)(react@19.2.6) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.15)(react@19.2.6) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.15)(react@19.2.6) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.15)(react@19.2.6) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.15)(react@19.2.6) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.15)(react@19.2.6) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.15)(react@19.2.6) + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) aria-hidden: 1.2.6 react: 19.2.6 react-dom: 19.2.6(react@19.2.6) - react-remove-scroll: 2.7.2(@types/react@19.2.14)(react@19.2.6) + react-remove-scroll: 2.7.2(@types/react@19.2.15)(react@19.2.6) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@types/react': 19.2.15 + '@types/react-dom': 19.2.3(@types/react@19.2.15) - '@radix-ui/react-slot@1.2.3(@types/react@19.2.14)(react@19.2.6)': + '@radix-ui/react-slot@1.2.3(@types/react@19.2.15)(react@19.2.6)': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.6) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.15)(react@19.2.6) react: 19.2.6 optionalDependencies: - '@types/react': 19.2.14 + '@types/react': 19.2.15 - '@radix-ui/react-slot@1.2.4(@types/react@19.2.14)(react@19.2.6)': + '@radix-ui/react-slot@1.2.4(@types/react@19.2.15)(react@19.2.6)': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.6) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.15)(react@19.2.6) react: 19.2.6 optionalDependencies: - '@types/react': 19.2.14 + '@types/react': 19.2.15 - '@radix-ui/react-tabs@1.1.13(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@radix-ui/react-tabs@1.1.13(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.6) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.14)(react@19.2.6) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.14)(react@19.2.6) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.14)(react@19.2.6) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.15)(react@19.2.6) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.15)(react@19.2.6) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.15)(react@19.2.6) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.15)(react@19.2.6) react: 19.2.6 react-dom: 19.2.6(react@19.2.6) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@types/react': 19.2.15 + '@types/react-dom': 19.2.3(@types/react@19.2.15) - '@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.2.14)(react@19.2.6)': + '@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.2.15)(react@19.2.6)': dependencies: react: 19.2.6 optionalDependencies: - '@types/react': 19.2.14 + '@types/react': 19.2.15 - '@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.2.14)(react@19.2.6)': + '@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.2.15)(react@19.2.6)': dependencies: - '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.14)(react@19.2.6) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.6) + '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.15)(react@19.2.6) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.15)(react@19.2.6) react: 19.2.6 optionalDependencies: - '@types/react': 19.2.14 + '@types/react': 19.2.15 - '@radix-ui/react-use-effect-event@0.0.2(@types/react@19.2.14)(react@19.2.6)': + '@radix-ui/react-use-effect-event@0.0.2(@types/react@19.2.15)(react@19.2.6)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.6) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.15)(react@19.2.6) react: 19.2.6 optionalDependencies: - '@types/react': 19.2.14 + '@types/react': 19.2.15 - '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@19.2.14)(react@19.2.6)': + '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@19.2.15)(react@19.2.6)': dependencies: - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.14)(react@19.2.6) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.15)(react@19.2.6) react: 19.2.6 optionalDependencies: - '@types/react': 19.2.14 + '@types/react': 19.2.15 - '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.2.14)(react@19.2.6)': + '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.2.15)(react@19.2.6)': dependencies: react: 19.2.6 optionalDependencies: - '@types/react': 19.2.14 + '@types/react': 19.2.15 - '@radix-ui/react-use-previous@1.1.1(@types/react@19.2.14)(react@19.2.6)': + '@radix-ui/react-use-previous@1.1.1(@types/react@19.2.15)(react@19.2.6)': dependencies: react: 19.2.6 optionalDependencies: - '@types/react': 19.2.14 + '@types/react': 19.2.15 - '@radix-ui/react-use-rect@1.1.1(@types/react@19.2.14)(react@19.2.6)': + '@radix-ui/react-use-rect@1.1.1(@types/react@19.2.15)(react@19.2.6)': dependencies: '@radix-ui/rect': 1.1.1 react: 19.2.6 optionalDependencies: - '@types/react': 19.2.14 + '@types/react': 19.2.15 - '@radix-ui/react-use-size@1.1.1(@types/react@19.2.14)(react@19.2.6)': + '@radix-ui/react-use-size@1.1.1(@types/react@19.2.15)(react@19.2.6)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.6) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.15)(react@19.2.6) react: 19.2.6 optionalDependencies: - '@types/react': 19.2.14 + '@types/react': 19.2.15 - '@radix-ui/react-visually-hidden@1.2.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@radix-ui/react-visually-hidden@1.2.3(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) react: 19.2.6 react-dom: 19.2.6(react@19.2.6) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@types/react': 19.2.15 + '@types/react-dom': 19.2.3(@types/react@19.2.15) '@radix-ui/rect@1.1.1': {} - '@rolldown/binding-android-arm64@1.0.1': + '@rolldown/binding-android-arm64@1.0.2': optional: true - '@rolldown/binding-darwin-arm64@1.0.1': + '@rolldown/binding-darwin-arm64@1.0.2': optional: true - '@rolldown/binding-darwin-x64@1.0.1': + '@rolldown/binding-darwin-x64@1.0.2': optional: true - '@rolldown/binding-freebsd-x64@1.0.1': + '@rolldown/binding-freebsd-x64@1.0.2': optional: true - '@rolldown/binding-linux-arm-gnueabihf@1.0.1': + '@rolldown/binding-linux-arm-gnueabihf@1.0.2': optional: true - '@rolldown/binding-linux-arm64-gnu@1.0.1': + '@rolldown/binding-linux-arm64-gnu@1.0.2': optional: true - '@rolldown/binding-linux-arm64-musl@1.0.1': + '@rolldown/binding-linux-arm64-musl@1.0.2': optional: true - '@rolldown/binding-linux-ppc64-gnu@1.0.1': + '@rolldown/binding-linux-ppc64-gnu@1.0.2': optional: true - '@rolldown/binding-linux-s390x-gnu@1.0.1': + '@rolldown/binding-linux-s390x-gnu@1.0.2': optional: true - '@rolldown/binding-linux-x64-gnu@1.0.1': + '@rolldown/binding-linux-x64-gnu@1.0.2': optional: true - '@rolldown/binding-linux-x64-musl@1.0.1': + '@rolldown/binding-linux-x64-musl@1.0.2': optional: true - '@rolldown/binding-openharmony-arm64@1.0.1': + '@rolldown/binding-openharmony-arm64@1.0.2': optional: true - '@rolldown/binding-wasm32-wasi@1.0.1': + '@rolldown/binding-wasm32-wasi@1.0.2': dependencies: '@emnapi/core': 1.10.0 '@emnapi/runtime': 1.10.0 '@napi-rs/wasm-runtime': 1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) optional: true - '@rolldown/binding-win32-arm64-msvc@1.0.1': + '@rolldown/binding-win32-arm64-msvc@1.0.2': optional: true - '@rolldown/binding-win32-x64-msvc@1.0.1': + '@rolldown/binding-win32-x64-msvc@1.0.2': optional: true '@rolldown/pluginutils@1.0.1': {} @@ -5013,13 +5053,13 @@ snapshots: '@alloc/quick-lru': 5.2.0 '@tailwindcss/node': 4.3.0 '@tailwindcss/oxide': 4.3.0 - postcss: 8.5.14 + postcss: 8.5.15 tailwindcss: 4.3.0 '@testing-library/dom@10.4.1': dependencies: - '@babel/code-frame': 7.29.0 - '@babel/runtime': 7.29.2 + '@babel/code-frame': 7.29.7 + '@babel/runtime': 7.29.7 '@types/aria-query': 5.0.4 aria-query: 5.3.0 dom-accessibility-api: 0.5.16 @@ -5036,15 +5076,15 @@ snapshots: picocolors: 1.1.1 redent: 3.0.0 - '@testing-library/react@16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@testing-library/react@16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: '@babel/runtime': 7.29.2 '@testing-library/dom': 10.4.1 react: 19.2.6 react-dom: 19.2.6(react@19.2.6) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@types/react': 19.2.15 + '@types/react-dom': 19.2.3(@types/react@19.2.15) '@tybys/wasm-util@0.10.1': dependencies: @@ -5067,6 +5107,8 @@ snapshots: '@types/estree@1.0.8': {} + '@types/estree@1.0.9': {} + '@types/json-schema@7.0.15': {} '@types/json5@0.0.29': {} @@ -5075,15 +5117,15 @@ snapshots: dependencies: undici-types: 6.21.0 - '@types/node@25.8.0': + '@types/node@25.9.1': dependencies: undici-types: 7.24.6 - '@types/react-dom@19.2.3(@types/react@19.2.14)': + '@types/react-dom@19.2.3(@types/react@19.2.15)': dependencies: - '@types/react': 19.2.14 + '@types/react': 19.2.15 - '@types/react@19.2.14': + '@types/react@19.2.15': dependencies: csstype: 3.2.3 @@ -5237,16 +5279,16 @@ snapshots: '@unrs/resolver-binding-win32-x64-msvc@1.11.1': optional: true - '@vitejs/plugin-react@6.0.2(vite@8.0.13(@types/node@25.8.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.1))': + '@vitejs/plugin-react@6.0.2(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.3))': dependencies: '@rolldown/pluginutils': 1.0.1 - vite: 8.0.13(@types/node@25.8.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.1) + vite: 8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.3) - '@vitest/coverage-v8@4.1.6(vitest@4.1.6)': + '@vitest/coverage-v8@4.1.7(vitest@4.1.7)': dependencies: '@bcoe/v8-coverage': 1.0.2 - '@vitest/utils': 4.1.6 - ast-v8-to-istanbul: 1.0.0 + '@vitest/utils': 4.1.7 + ast-v8-to-istanbul: 1.0.2 istanbul-lib-coverage: 3.2.2 istanbul-lib-report: 3.0.1 istanbul-reports: 3.2.0 @@ -5254,46 +5296,46 @@ snapshots: obug: 2.1.1 std-env: 4.1.0 tinyrainbow: 3.1.0 - vitest: 4.1.6(@opentelemetry/api@1.9.0)(@types/node@25.8.0)(@vitest/coverage-v8@4.1.6)(jsdom@29.1.1(@noble/hashes@2.0.1))(vite@8.0.13(@types/node@25.8.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.1)) + vitest: 4.1.7(@opentelemetry/api@1.9.0)(@types/node@25.9.1)(@vitest/coverage-v8@4.1.7)(jsdom@29.1.1(@noble/hashes@2.2.0))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.3)) - '@vitest/expect@4.1.6': + '@vitest/expect@4.1.7': dependencies: '@standard-schema/spec': 1.1.0 '@types/chai': 5.2.3 - '@vitest/spy': 4.1.6 - '@vitest/utils': 4.1.6 + '@vitest/spy': 4.1.7 + '@vitest/utils': 4.1.7 chai: 6.2.2 tinyrainbow: 3.1.0 - '@vitest/mocker@4.1.6(vite@8.0.13(@types/node@25.8.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.1))': + '@vitest/mocker@4.1.7(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.3))': dependencies: - '@vitest/spy': 4.1.6 + '@vitest/spy': 4.1.7 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 8.0.13(@types/node@25.8.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.1) + vite: 8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.3) - '@vitest/pretty-format@4.1.6': + '@vitest/pretty-format@4.1.7': dependencies: tinyrainbow: 3.1.0 - '@vitest/runner@4.1.6': + '@vitest/runner@4.1.7': dependencies: - '@vitest/utils': 4.1.6 + '@vitest/utils': 4.1.7 pathe: 2.0.3 - '@vitest/snapshot@4.1.6': + '@vitest/snapshot@4.1.7': dependencies: - '@vitest/pretty-format': 4.1.6 - '@vitest/utils': 4.1.6 + '@vitest/pretty-format': 4.1.7 + '@vitest/utils': 4.1.7 magic-string: 0.30.21 pathe: 2.0.3 - '@vitest/spy@4.1.6': {} + '@vitest/spy@4.1.7': {} - '@vitest/utils@4.1.6': + '@vitest/utils@4.1.7': dependencies: - '@vitest/pretty-format': 4.1.6 + '@vitest/pretty-format': 4.1.7 convert-source-map: 2.0.0 tinyrainbow: 3.1.0 @@ -5401,7 +5443,7 @@ snapshots: ast-types-flow@0.0.8: {} - ast-v8-to-istanbul@1.0.0: + ast-v8-to-istanbul@1.0.2: dependencies: '@jridgewell/trace-mapping': 0.3.31 estree-walker: 3.0.3 @@ -5409,13 +5451,13 @@ snapshots: async-function@1.0.0: {} - autoprefixer@10.5.0(postcss@8.5.14): + autoprefixer@10.5.0(postcss@8.5.15): dependencies: browserslist: 4.28.2 caniuse-lite: 1.0.30001793 fraction.js: 5.3.4 picocolors: 1.1.1 - postcss: 8.5.14 + postcss: 8.5.15 postcss-value-parser: 4.2.0 available-typed-arrays@1.0.7: @@ -5434,37 +5476,37 @@ snapshots: baseline-browser-mapping@2.10.30: {} - better-auth@1.6.11(@opentelemetry/api@1.9.0)(next@16.2.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.60.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.6): + better-auth@1.6.12(@opentelemetry/api@1.9.0)(next@16.2.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.60.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7): dependencies: - '@better-auth/core': 1.6.11(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.2.0) - '@better-auth/drizzle-adapter': 1.6.11(@better-auth/core@1.6.11(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.2.0))(@better-auth/utils@0.4.0) - '@better-auth/kysely-adapter': 1.6.11(@better-auth/core@1.6.11(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.2.0))(@better-auth/utils@0.4.0)(kysely@0.28.17) - '@better-auth/memory-adapter': 1.6.11(@better-auth/core@1.6.11(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.2.0))(@better-auth/utils@0.4.0) - '@better-auth/mongo-adapter': 1.6.11(@better-auth/core@1.6.11(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.2.0))(@better-auth/utils@0.4.0) - '@better-auth/prisma-adapter': 1.6.11(@better-auth/core@1.6.11(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.2.0))(@better-auth/utils@0.4.0) - '@better-auth/telemetry': 1.6.11(@better-auth/core@1.6.11(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.2.0))(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21) - '@better-auth/utils': 0.4.0 + '@better-auth/core': 1.6.12(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.3.0) + '@better-auth/drizzle-adapter': 1.6.12(@better-auth/core@1.6.12(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.3.0))(@better-auth/utils@0.4.1) + '@better-auth/kysely-adapter': 1.6.12(@better-auth/core@1.6.12(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.3.0))(@better-auth/utils@0.4.1)(kysely@0.28.17) + '@better-auth/memory-adapter': 1.6.12(@better-auth/core@1.6.12(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.3.0))(@better-auth/utils@0.4.1) + '@better-auth/mongo-adapter': 1.6.12(@better-auth/core@1.6.12(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.3.0))(@better-auth/utils@0.4.1) + '@better-auth/prisma-adapter': 1.6.12(@better-auth/core@1.6.12(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.3.0))(@better-auth/utils@0.4.1) + '@better-auth/telemetry': 1.6.12(@better-auth/core@1.6.12(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.3.0))(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.1.21) + '@better-auth/utils': 0.4.1 '@better-fetch/fetch': 1.1.21 - '@noble/ciphers': 2.1.1 - '@noble/hashes': 2.0.1 + '@noble/ciphers': 2.2.0 + '@noble/hashes': 2.2.0 better-call: 1.3.5(zod@4.4.3) defu: 6.1.7 jose: 6.2.3 kysely: 0.28.17 - nanostores: 1.2.0 + nanostores: 1.3.0 zod: 4.4.3 optionalDependencies: next: 16.2.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.60.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) react: 19.2.6 react-dom: 19.2.6(react@19.2.6) - vitest: 4.1.6(@opentelemetry/api@1.9.0)(@types/node@25.8.0)(@vitest/coverage-v8@4.1.6)(jsdom@29.1.1(@noble/hashes@2.0.1))(vite@8.0.13(@types/node@25.8.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.1)) + vitest: 4.1.7(@opentelemetry/api@1.9.0)(@types/node@25.9.1)(@vitest/coverage-v8@4.1.7)(jsdom@29.1.1(@noble/hashes@2.2.0))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.3)) transitivePeerDependencies: - '@cloudflare/workers-types' - '@opentelemetry/api' better-call@1.3.5(zod@4.4.3): dependencies: - '@better-auth/utils': 0.4.0 + '@better-auth/utils': 0.4.1 '@better-fetch/fetch': 1.1.21 rou3: 0.7.12 set-cookie-parser: 3.1.0 @@ -5475,7 +5517,7 @@ snapshots: dependencies: require-from-string: 2.0.2 - brace-expansion@1.1.12: + brace-expansion@1.1.15: dependencies: balanced-match: 1.0.2 concat-map: 0.0.1 @@ -5590,10 +5632,10 @@ snapshots: damerau-levenshtein@1.0.8: {} - data-urls@7.0.0(@noble/hashes@2.0.1): + data-urls@7.0.0(@noble/hashes@2.2.0): dependencies: whatwg-mimetype: 5.0.0 - whatwg-url: 16.0.1(@noble/hashes@2.0.1) + whatwg-url: 16.0.1(@noble/hashes@2.2.0) transitivePeerDependencies: - '@noble/hashes' @@ -6020,7 +6062,7 @@ snapshots: estree-walker@3.0.3: dependencies: - '@types/estree': 1.0.8 + '@types/estree': 1.0.9 esutils@2.0.3: {} @@ -6046,7 +6088,7 @@ snapshots: dependencies: fast-string-truncated-width: 3.0.3 - fast-wrap-ansi@0.2.0: + fast-wrap-ansi@0.2.2: dependencies: fast-string-width: 3.0.2 @@ -6188,9 +6230,9 @@ snapshots: dependencies: hermes-estree: 0.25.1 - html-encoding-sniffer@6.0.0(@noble/hashes@2.0.1): + html-encoding-sniffer@6.0.0(@noble/hashes@2.2.0): dependencies: - '@exodus/bytes': 1.15.0(@noble/hashes@2.0.1) + '@exodus/bytes': 1.15.0(@noble/hashes@2.2.0) transitivePeerDependencies: - '@noble/hashes' @@ -6373,17 +6415,17 @@ snapshots: dependencies: argparse: 2.0.1 - jsdom@29.1.1(@noble/hashes@2.0.1): + jsdom@29.1.1(@noble/hashes@2.2.0): dependencies: '@asamuzakjp/css-color': 5.1.11 '@asamuzakjp/dom-selector': 7.1.1 '@bramus/specificity': 2.4.2 '@csstools/css-syntax-patches-for-csstree': 1.1.4(css-tree@3.2.1) - '@exodus/bytes': 1.15.0(@noble/hashes@2.0.1) + '@exodus/bytes': 1.15.0(@noble/hashes@2.2.0) css-tree: 3.2.1 - data-urls: 7.0.0(@noble/hashes@2.0.1) + data-urls: 7.0.0(@noble/hashes@2.2.0) decimal.js: 10.6.0 - html-encoding-sniffer: 6.0.0(@noble/hashes@2.0.1) + html-encoding-sniffer: 6.0.0(@noble/hashes@2.2.0) is-potential-custom-element-name: 1.0.1 lru-cache: 11.3.6 parse5: 8.0.1 @@ -6394,7 +6436,7 @@ snapshots: w3c-xmlserializer: 5.0.0 webidl-conversions: 8.0.1 whatwg-mimetype: 5.0.0 - whatwg-url: 16.0.1(@noble/hashes@2.0.1) + whatwg-url: 16.0.1(@noble/hashes@2.2.0) xml-name-validator: 5.0.0 transitivePeerDependencies: - '@noble/hashes' @@ -6502,7 +6544,7 @@ snapshots: dependencies: yallist: 3.1.1 - lucide-react@1.16.0(react@19.2.6): + lucide-react@1.17.0(react@19.2.6): dependencies: react: 19.2.6 @@ -6514,13 +6556,13 @@ snapshots: magicast@0.5.3: dependencies: - '@babel/parser': 7.29.3 - '@babel/types': 7.29.0 + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 source-map-js: 1.2.1 make-dir@4.0.0: dependencies: - semver: 7.7.4 + semver: 7.8.1 math-intrinsics@1.1.0: {} @@ -6541,7 +6583,7 @@ snapshots: minimatch@3.1.5: dependencies: - brace-expansion: 1.1.12 + brace-expansion: 1.1.15 minimist@1.2.8: {} @@ -6549,9 +6591,9 @@ snapshots: mute-stream@3.0.0: {} - nanoid@3.3.11: {} + nanoid@3.3.12: {} - nanostores@1.2.0: {} + nanostores@1.3.0: {} napi-postinstall@0.3.4: {} @@ -6563,7 +6605,7 @@ snapshots: '@swc/helpers': 0.5.15 baseline-browser-mapping: 2.10.10 caniuse-lite: 1.0.30001781 - postcss: 8.4.31 + postcss: 8.5.15 react: 19.2.6 react-dom: 19.2.6(react@19.2.6) styled-jsx: 5.1.6(@babel/core@7.29.0)(react@19.2.6) @@ -6695,15 +6737,9 @@ snapshots: postcss-value-parser@4.2.0: {} - postcss@8.4.31: - dependencies: - nanoid: 3.3.11 - picocolors: 1.1.1 - source-map-js: 1.2.1 - - postcss@8.5.14: + postcss@8.5.15: dependencies: - nanoid: 3.3.11 + nanoid: 3.3.12 picocolors: 1.1.1 source-map-js: 1.2.1 @@ -6730,7 +6766,7 @@ snapshots: react: 19.2.6 scheduler: 0.27.0 - react-hook-form@7.76.0(react@19.2.6): + react-hook-form@7.76.1(react@19.2.6): dependencies: react: 19.2.6 @@ -6738,32 +6774,32 @@ snapshots: react-is@17.0.2: {} - react-remove-scroll-bar@2.3.8(@types/react@19.2.14)(react@19.2.6): + react-remove-scroll-bar@2.3.8(@types/react@19.2.15)(react@19.2.6): dependencies: react: 19.2.6 - react-style-singleton: 2.2.3(@types/react@19.2.14)(react@19.2.6) + react-style-singleton: 2.2.3(@types/react@19.2.15)(react@19.2.6) tslib: 2.8.1 optionalDependencies: - '@types/react': 19.2.14 + '@types/react': 19.2.15 - react-remove-scroll@2.7.2(@types/react@19.2.14)(react@19.2.6): + react-remove-scroll@2.7.2(@types/react@19.2.15)(react@19.2.6): dependencies: react: 19.2.6 - react-remove-scroll-bar: 2.3.8(@types/react@19.2.14)(react@19.2.6) - react-style-singleton: 2.2.3(@types/react@19.2.14)(react@19.2.6) + react-remove-scroll-bar: 2.3.8(@types/react@19.2.15)(react@19.2.6) + react-style-singleton: 2.2.3(@types/react@19.2.15)(react@19.2.6) tslib: 2.8.1 - use-callback-ref: 1.3.3(@types/react@19.2.14)(react@19.2.6) - use-sidecar: 1.1.3(@types/react@19.2.14)(react@19.2.6) + use-callback-ref: 1.3.3(@types/react@19.2.15)(react@19.2.6) + use-sidecar: 1.1.3(@types/react@19.2.15)(react@19.2.6) optionalDependencies: - '@types/react': 19.2.14 + '@types/react': 19.2.15 - react-style-singleton@2.2.3(@types/react@19.2.14)(react@19.2.6): + react-style-singleton@2.2.3(@types/react@19.2.15)(react@19.2.6): dependencies: get-nonce: 1.0.1 react: 19.2.6 tslib: 2.8.1 optionalDependencies: - '@types/react': 19.2.14 + '@types/react': 19.2.15 react@19.2.6: {} @@ -6817,26 +6853,26 @@ snapshots: reusify@1.1.0: {} - rolldown@1.0.1: + rolldown@1.0.2: dependencies: - '@oxc-project/types': 0.130.0 + '@oxc-project/types': 0.132.0 '@rolldown/pluginutils': 1.0.1 optionalDependencies: - '@rolldown/binding-android-arm64': 1.0.1 - '@rolldown/binding-darwin-arm64': 1.0.1 - '@rolldown/binding-darwin-x64': 1.0.1 - '@rolldown/binding-freebsd-x64': 1.0.1 - '@rolldown/binding-linux-arm-gnueabihf': 1.0.1 - '@rolldown/binding-linux-arm64-gnu': 1.0.1 - '@rolldown/binding-linux-arm64-musl': 1.0.1 - '@rolldown/binding-linux-ppc64-gnu': 1.0.1 - '@rolldown/binding-linux-s390x-gnu': 1.0.1 - '@rolldown/binding-linux-x64-gnu': 1.0.1 - '@rolldown/binding-linux-x64-musl': 1.0.1 - '@rolldown/binding-openharmony-arm64': 1.0.1 - '@rolldown/binding-wasm32-wasi': 1.0.1 - '@rolldown/binding-win32-arm64-msvc': 1.0.1 - '@rolldown/binding-win32-x64-msvc': 1.0.1 + '@rolldown/binding-android-arm64': 1.0.2 + '@rolldown/binding-darwin-arm64': 1.0.2 + '@rolldown/binding-darwin-x64': 1.0.2 + '@rolldown/binding-freebsd-x64': 1.0.2 + '@rolldown/binding-linux-arm-gnueabihf': 1.0.2 + '@rolldown/binding-linux-arm64-gnu': 1.0.2 + '@rolldown/binding-linux-arm64-musl': 1.0.2 + '@rolldown/binding-linux-ppc64-gnu': 1.0.2 + '@rolldown/binding-linux-s390x-gnu': 1.0.2 + '@rolldown/binding-linux-x64-gnu': 1.0.2 + '@rolldown/binding-linux-x64-musl': 1.0.2 + '@rolldown/binding-openharmony-arm64': 1.0.2 + '@rolldown/binding-wasm32-wasi': 1.0.2 + '@rolldown/binding-win32-arm64-msvc': 1.0.2 + '@rolldown/binding-win32-x64-msvc': 1.0.2 rou3@0.7.12: {} @@ -6879,6 +6915,8 @@ snapshots: semver@7.7.4: {} + semver@7.8.1: {} + set-cookie-parser@3.1.0: {} set-function-length@1.2.2: @@ -7083,14 +7121,14 @@ snapshots: tinybench@2.9.0: {} - tinyexec@1.1.2: {} + tinyexec@1.2.3: {} tinyglobby@0.2.15: dependencies: fdir: 6.5.0(picomatch@4.0.4) picomatch: 4.0.4 - tinyglobby@0.2.16: + tinyglobby@0.2.17: dependencies: fdir: 6.5.0(picomatch@4.0.4) picomatch: 4.0.4 @@ -7130,7 +7168,7 @@ snapshots: tslib@2.8.1: {} - tsx@4.22.1: + tsx@4.22.3: dependencies: esbuild: 0.28.0 optionalDependencies: @@ -7243,58 +7281,58 @@ snapshots: dependencies: punycode: 2.3.1 - use-callback-ref@1.3.3(@types/react@19.2.14)(react@19.2.6): + use-callback-ref@1.3.3(@types/react@19.2.15)(react@19.2.6): dependencies: react: 19.2.6 tslib: 2.8.1 optionalDependencies: - '@types/react': 19.2.14 + '@types/react': 19.2.15 - use-sidecar@1.1.3(@types/react@19.2.14)(react@19.2.6): + use-sidecar@1.1.3(@types/react@19.2.15)(react@19.2.6): dependencies: detect-node-es: 1.1.0 react: 19.2.6 tslib: 2.8.1 optionalDependencies: - '@types/react': 19.2.14 + '@types/react': 19.2.15 - vite@8.0.13(@types/node@20.19.37)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.1): + vite@8.0.14(@types/node@20.19.37)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.3): dependencies: lightningcss: 1.32.0 picomatch: 4.0.4 - postcss: 8.5.14 - rolldown: 1.0.1 - tinyglobby: 0.2.16 + postcss: 8.5.15 + rolldown: 1.0.2 + tinyglobby: 0.2.17 optionalDependencies: '@types/node': 20.19.37 esbuild: 0.28.0 fsevents: 2.3.3 jiti: 2.7.0 - tsx: 4.22.1 + tsx: 4.22.3 - vite@8.0.13(@types/node@25.8.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.1): + vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.3): dependencies: lightningcss: 1.32.0 picomatch: 4.0.4 - postcss: 8.5.14 - rolldown: 1.0.1 - tinyglobby: 0.2.16 + postcss: 8.5.15 + rolldown: 1.0.2 + tinyglobby: 0.2.17 optionalDependencies: - '@types/node': 25.8.0 + '@types/node': 25.9.1 esbuild: 0.28.0 fsevents: 2.3.3 jiti: 2.7.0 - tsx: 4.22.1 + tsx: 4.22.3 - vitest@4.1.6(@opentelemetry/api@1.9.0)(@types/node@25.8.0)(@vitest/coverage-v8@4.1.6)(jsdom@29.1.1(@noble/hashes@2.0.1))(vite@8.0.13(@types/node@25.8.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.1)): + vitest@4.1.7(@opentelemetry/api@1.9.0)(@types/node@25.9.1)(@vitest/coverage-v8@4.1.7)(jsdom@29.1.1(@noble/hashes@2.2.0))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.3)): dependencies: - '@vitest/expect': 4.1.6 - '@vitest/mocker': 4.1.6(vite@8.0.13(@types/node@25.8.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.1)) - '@vitest/pretty-format': 4.1.6 - '@vitest/runner': 4.1.6 - '@vitest/snapshot': 4.1.6 - '@vitest/spy': 4.1.6 - '@vitest/utils': 4.1.6 + '@vitest/expect': 4.1.7 + '@vitest/mocker': 4.1.7(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.3)) + '@vitest/pretty-format': 4.1.7 + '@vitest/runner': 4.1.7 + '@vitest/snapshot': 4.1.7 + '@vitest/spy': 4.1.7 + '@vitest/utils': 4.1.7 es-module-lexer: 2.1.0 expect-type: 1.3.0 magic-string: 0.30.21 @@ -7303,16 +7341,16 @@ snapshots: picomatch: 4.0.4 std-env: 4.1.0 tinybench: 2.9.0 - tinyexec: 1.1.2 - tinyglobby: 0.2.16 + tinyexec: 1.2.3 + tinyglobby: 0.2.17 tinyrainbow: 3.1.0 - vite: 8.0.13(@types/node@25.8.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.1) + vite: 8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.3) why-is-node-running: 2.3.0 optionalDependencies: '@opentelemetry/api': 1.9.0 - '@types/node': 25.8.0 - '@vitest/coverage-v8': 4.1.6(vitest@4.1.6) - jsdom: 29.1.1(@noble/hashes@2.0.1) + '@types/node': 25.9.1 + '@vitest/coverage-v8': 4.1.7(vitest@4.1.7) + jsdom: 29.1.1(@noble/hashes@2.2.0) transitivePeerDependencies: - msw @@ -7324,9 +7362,9 @@ snapshots: whatwg-mimetype@5.0.0: {} - whatwg-url@16.0.1(@noble/hashes@2.0.1): + whatwg-url@16.0.1(@noble/hashes@2.2.0): dependencies: - '@exodus/bytes': 1.15.0(@noble/hashes@2.0.1) + '@exodus/bytes': 1.15.0(@noble/hashes@2.2.0) tr46: 6.0.0 webidl-conversions: 8.0.1 transitivePeerDependencies: @@ -7416,6 +7454,4 @@ snapshots: dependencies: zod: 4.4.3 - zod@4.3.6: {} - zod@4.4.3: {} From 8716b294489b84a6c90f16d145f97d32f24f0a19 Mon Sep 17 00:00:00 2001 From: Chris Kehayias Date: Sat, 30 May 2026 17:06:14 -0400 Subject: [PATCH 07/20] chore(deps): bump @types/node to 22.19.19 in @mpnext/embed-sdk Raise the SDK's @types/node from ^20.19.37 to ^22.19.19 (latest 22.x, Node 22 LTS), closing the gap with the Node 20 baseline. Intentionally kept on the 22.x line rather than matching the root app's @types/node 25, since the SDK targets a Node 22 LTS toolchain. Dev-only type dependency for the Vite/TS toolchain; SDK source uses DOM/Web Component APIs, not Node globals, so no type surface is affected. Verified: tsc typecheck clean, vite build green (bundle byte-identical, 161.81 kB / 33.95 kB gzip), pnpm test:run 530/530 passing. Co-Authored-By: Claude Opus 4.8 (1M context) --- packages/embed-sdk/package.json | 2 +- pnpm-lock.yaml | 32 ++++++++++++++++---------------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/packages/embed-sdk/package.json b/packages/embed-sdk/package.json index cc0be53..3b63c73 100644 --- a/packages/embed-sdk/package.json +++ b/packages/embed-sdk/package.json @@ -20,7 +20,7 @@ "preview": "vite preview" }, "devDependencies": { - "@types/node": "^20.17.10", + "@types/node": "^22.19.19", "typescript": "^6", "vite": "^8.0.14" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6809a1b..e918399 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -149,14 +149,14 @@ importers: packages/embed-sdk: devDependencies: '@types/node': - specifier: ^20.17.10 - version: 20.19.37 + specifier: ^22.19.19 + version: 22.19.19 typescript: specifier: ^6 version: 6.0.2 vite: specifier: ^8.0.14 - version: 8.0.14(@types/node@20.19.37)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.3) + version: 8.0.14(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.3) packages/types: dependencies: @@ -1700,8 +1700,8 @@ packages: '@types/json5@0.0.29': resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} - '@types/node@20.19.37': - resolution: {integrity: sha512-8kzdPJ3FsNsVIurqBs7oodNnCEVbni9yUEkaHbgptDACOPW04jimGagZ51E6+lXUwJjgnBw+hyko/lkFWCldqw==} + '@types/node@22.19.19': + resolution: {integrity: sha512-dyh/xO2Fh5bYrfWaaqGrRQQGkNdmYw6AmaAUvYeUMNTWQtvb796ikLdmTchRmOlOiIJ1TDXfWgVx1QkUlQ6Hew==} '@types/node@25.9.1': resolution: {integrity: sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg==} @@ -5113,7 +5113,7 @@ snapshots: '@types/json5@0.0.29': {} - '@types/node@20.19.37': + '@types/node@22.19.19': dependencies: undici-types: 6.21.0 @@ -5862,8 +5862,8 @@ snapshots: '@next/eslint-plugin-next': 16.2.6 eslint: 9.39.4(jiti@2.7.0) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.4(jiti@2.7.0)) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.57.2(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.7.0)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.57.2(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.4(jiti@2.7.0)))(eslint@9.39.4(jiti@2.7.0)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.57.2(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.57.2(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.4(jiti@2.7.0)))(eslint@9.39.4(jiti@2.7.0)))(eslint@9.39.4(jiti@2.7.0)) eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.4(jiti@2.7.0)) eslint-plugin-react: 7.37.5(eslint@9.39.4(jiti@2.7.0)) eslint-plugin-react-hooks: 7.0.1(eslint@9.39.4(jiti@2.7.0)) @@ -5885,7 +5885,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.4(jiti@2.7.0)): + eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.57.2(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.4(jiti@2.7.0)))(eslint@9.39.4(jiti@2.7.0)): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.4.3 @@ -5896,22 +5896,22 @@ snapshots: tinyglobby: 0.2.15 unrs-resolver: 1.11.1 optionalDependencies: - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.57.2(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.7.0)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.57.2(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.57.2(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.4(jiti@2.7.0)))(eslint@9.39.4(jiti@2.7.0)))(eslint@9.39.4(jiti@2.7.0)) transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@8.57.2(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.7.0)): + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.57.2(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.57.2(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.4(jiti@2.7.0)))(eslint@9.39.4(jiti@2.7.0)))(eslint@9.39.4(jiti@2.7.0)): dependencies: debug: 3.2.7 optionalDependencies: '@typescript-eslint/parser': 8.57.2(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3) eslint: 9.39.4(jiti@2.7.0) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.4(jiti@2.7.0)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.57.2(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.4(jiti@2.7.0)))(eslint@9.39.4(jiti@2.7.0)) transitivePeerDependencies: - supports-color - eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.57.2(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.7.0)): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.57.2(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.57.2(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.4(jiti@2.7.0)))(eslint@9.39.4(jiti@2.7.0)))(eslint@9.39.4(jiti@2.7.0)): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -5922,7 +5922,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.39.4(jiti@2.7.0) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.57.2(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.7.0)) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.57.2(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.57.2(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.4(jiti@2.7.0)))(eslint@9.39.4(jiti@2.7.0)))(eslint@9.39.4(jiti@2.7.0)) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -7296,7 +7296,7 @@ snapshots: optionalDependencies: '@types/react': 19.2.15 - vite@8.0.14(@types/node@20.19.37)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.3): + vite@8.0.14(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.3): dependencies: lightningcss: 1.32.0 picomatch: 4.0.4 @@ -7304,7 +7304,7 @@ snapshots: rolldown: 1.0.2 tinyglobby: 0.2.17 optionalDependencies: - '@types/node': 20.19.37 + '@types/node': 22.19.19 esbuild: 0.28.0 fsevents: 2.3.3 jiti: 2.7.0 From 8d73450c9c7e03ee9b15625ff3806b03e7c23fff Mon Sep 17 00:00:00 2001 From: Chris Kehayias Date: Sat, 30 May 2026 17:10:35 -0400 Subject: [PATCH 08/20] chore(deps): bump typescript to 6.0.3 in workspace packages Align @mpnext/embed-sdk and @mpnext/types from ^6 (resolved 6.0.2) to 6.0.3, matching the root app. Patch-level, dev-only. Verified: pnpm build (SDK + Next) green, pnpm test:run 530/530 passing. Co-Authored-By: Claude Opus 4.8 (1M context) --- packages/embed-sdk/package.json | 2 +- packages/types/package.json | 2 +- pnpm-lock.yaml | 15 ++++----------- 3 files changed, 6 insertions(+), 13 deletions(-) diff --git a/packages/embed-sdk/package.json b/packages/embed-sdk/package.json index 3b63c73..598b747 100644 --- a/packages/embed-sdk/package.json +++ b/packages/embed-sdk/package.json @@ -21,7 +21,7 @@ }, "devDependencies": { "@types/node": "^22.19.19", - "typescript": "^6", + "typescript": "^6.0.3", "vite": "^8.0.14" } } diff --git a/packages/types/package.json b/packages/types/package.json index dbe966e..7b16bf6 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -17,7 +17,7 @@ "build": "tsc" }, "devDependencies": { - "typescript": "^6" + "typescript": "^6.0.3" }, "dependencies": { "zod": "^4.4.3" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e918399..8192112 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -152,8 +152,8 @@ importers: specifier: ^22.19.19 version: 22.19.19 typescript: - specifier: ^6 - version: 6.0.2 + specifier: ^6.0.3 + version: 6.0.3 vite: specifier: ^8.0.14 version: 8.0.14(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.3) @@ -165,8 +165,8 @@ importers: version: 4.4.3 devDependencies: typescript: - specifier: ^6 - version: 6.0.2 + specifier: ^6.0.3 + version: 6.0.3 packages: @@ -3615,11 +3615,6 @@ packages: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.0.0' - typescript@6.0.2: - resolution: {integrity: sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ==} - engines: {node: '>=14.17'} - hasBin: true - typescript@6.0.3: resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==} engines: {node: '>=14.17'} @@ -7224,8 +7219,6 @@ snapshots: transitivePeerDependencies: - supports-color - typescript@6.0.2: {} - typescript@6.0.3: {} unbox-primitive@1.1.0: From 5091448d0766bd17afb7902130f87b4f668a4526 Mon Sep 17 00:00:00 2001 From: Chris Kehayias Date: Sat, 30 May 2026 17:12:30 -0400 Subject: [PATCH 09/20] chore(deps): upgrade concurrently 9.2.1 -> 10.0.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dev-only task runner used by the dev and test:widget scripts. v10's only notable breaking change is dropping Node <20 (CI/dev run Node 24). Verified: smoke-tested the exact flags the scripts use (concurrently -n next,widget -c blue,green ...) — both processes labeled and exit 0. pnpm build (SDK + Next) green, pnpm test:run 530/530 passing. Co-Authored-By: Claude Opus 4.8 (1M context) --- package.json | 2 +- pnpm-lock.yaml | 167 +++++++++++++++++++++++++------------------------ 2 files changed, 86 insertions(+), 83 deletions(-) diff --git a/package.json b/package.json index f6f6f83..c628471 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,7 @@ "@vitest/coverage-v8": "^4.1.7", "autoprefixer": "^10.5.0", "chalk": "^5.6.2", - "concurrently": "^9.2.1", + "concurrently": "^10.0.0", "eslint": "^9", "eslint-config-next": "^16.2.6", "jsdom": "^29.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8192112..579eeda 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -116,8 +116,8 @@ importers: specifier: ^5.6.2 version: 5.6.2 concurrently: - specifier: ^9.2.1 - version: 9.2.1 + specifier: ^10.0.0 + version: 10.0.0 eslint: specifier: ^9 version: 9.39.4(jiti@2.7.0) @@ -1944,6 +1944,10 @@ packages: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} + ansi-regex@6.2.2: + resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} + engines: {node: '>=12'} + ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} @@ -1952,6 +1956,10 @@ packages: resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} engines: {node: '>=10'} + ansi-styles@6.2.3: + resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} + engines: {node: '>=12'} + argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} @@ -2189,9 +2197,9 @@ packages: client-only@0.0.1: resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} - cliui@8.0.1: - resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} - engines: {node: '>=12'} + cliui@9.0.1: + resolution: {integrity: sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==} + engines: {node: '>=20'} clsx@2.1.1: resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} @@ -2207,9 +2215,9 @@ packages: concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - concurrently@9.2.1: - resolution: {integrity: sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng==} - engines: {node: '>=18'} + concurrently@10.0.0: + resolution: {integrity: sha512-DRrk10z3sVPpguNe8od2cGNqZGqbT15rwAnxD4dG3b78mdNNb/gJyr8T834Oj518WcBmTktrt4FhdwZn09ZWSg==} + engines: {node: '>=22'} hasBin: true convert-source-map@2.0.0: @@ -2317,8 +2325,8 @@ packages: electron-to-chromium@1.5.357: resolution: {integrity: sha512-NHlTIQDK8fmVwHwuIzmXYEJ1Ewq3D9wDNc0cWXxDGysP6Pb21giwGNkxiTifyKy/4SoPuN5l6GLP1W9Sv7zB2g==} - emoji-regex@8.0.0: - resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + emoji-regex@10.6.0: + resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} @@ -2598,6 +2606,10 @@ packages: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} + get-east-asian-width@1.6.0: + resolution: {integrity: sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==} + engines: {node: '>=18'} + get-intrinsic@1.3.0: resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} engines: {node: '>= 0.4'} @@ -2755,10 +2767,6 @@ packages: resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} engines: {node: '>= 0.4'} - is-fullwidth-code-point@3.0.0: - resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} - engines: {node: '>=8'} - is-generator-function@1.1.2: resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==} engines: {node: '>= 0.4'} @@ -3295,10 +3303,6 @@ packages: resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} engines: {node: '>= 0.4'} - require-directory@2.1.1: - resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} - engines: {node: '>=0.10.0'} - require-from-string@2.0.2: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} @@ -3401,8 +3405,8 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} - shell-quote@1.8.3: - resolution: {integrity: sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==} + shell-quote@1.8.4: + resolution: {integrity: sha512-VsC6n6vz1ihYYyZZwX7YZSF5l5x36ca17OC+a69h94YqB7X6XLwf+5MOgynYir2SLFUbl8gIYvBo8K8RoNQ6bQ==} engines: {node: '>= 0.4'} side-channel-list@1.0.0: @@ -3445,9 +3449,9 @@ packages: resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} engines: {node: '>= 0.4'} - string-width@4.2.3: - resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} - engines: {node: '>=8'} + string-width@7.2.0: + resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} + engines: {node: '>=18'} string.prototype.includes@2.0.1: resolution: {integrity: sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==} @@ -3472,9 +3476,9 @@ packages: resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} engines: {node: '>= 0.4'} - strip-ansi@6.0.1: - resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} - engines: {node: '>=8'} + strip-ansi@7.2.0: + resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} + engines: {node: '>=12'} strip-bom@3.0.0: resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} @@ -3501,14 +3505,14 @@ packages: babel-plugin-macros: optional: true + supports-color@10.2.2: + resolution: {integrity: sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==} + engines: {node: '>=18'} + supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} - supports-color@8.1.1: - resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} - engines: {node: '>=10'} - supports-preserve-symlinks-flag@1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} @@ -3796,9 +3800,9 @@ packages: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} - wrap-ansi@7.0.0: - resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} - engines: {node: '>=10'} + wrap-ansi@9.0.2: + resolution: {integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==} + engines: {node: '>=18'} xml-name-validator@5.0.0: resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} @@ -3814,13 +3818,13 @@ packages: yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} - yargs-parser@21.1.1: - resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} - engines: {node: '>=12'} + yargs-parser@22.0.0: + resolution: {integrity: sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==} + engines: {node: ^20.19.0 || ^22.12.0 || >=23} - yargs@17.7.2: - resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} - engines: {node: '>=12'} + yargs@18.0.0: + resolution: {integrity: sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==} + engines: {node: ^20.19.0 || ^22.12.0 || >=23} yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} @@ -5349,12 +5353,16 @@ snapshots: ansi-regex@5.0.1: {} + ansi-regex@6.2.2: {} + ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 ansi-styles@5.2.0: {} + ansi-styles@6.2.3: {} + argparse@2.0.1: {} aria-hidden@1.2.6: @@ -5583,11 +5591,11 @@ snapshots: client-only@0.0.1: {} - cliui@8.0.1: + cliui@9.0.1: dependencies: - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi: 7.0.0 + string-width: 7.2.0 + strip-ansi: 7.2.0 + wrap-ansi: 9.0.2 clsx@2.1.1: {} @@ -5599,14 +5607,14 @@ snapshots: concat-map@0.0.1: {} - concurrently@9.2.1: + concurrently@10.0.0: dependencies: - chalk: 4.1.2 + chalk: 5.6.2 rxjs: 7.8.2 - shell-quote: 1.8.3 - supports-color: 8.1.1 + shell-quote: 1.8.4 + supports-color: 10.2.2 tree-kill: 1.2.2 - yargs: 17.7.2 + yargs: 18.0.0 convert-source-map@2.0.0: {} @@ -5704,7 +5712,7 @@ snapshots: electron-to-chromium@1.5.357: {} - emoji-regex@8.0.0: {} + emoji-regex@10.6.0: {} emoji-regex@9.2.2: {} @@ -5857,8 +5865,8 @@ snapshots: '@next/eslint-plugin-next': 16.2.6 eslint: 9.39.4(jiti@2.7.0) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.57.2(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.4(jiti@2.7.0)))(eslint@9.39.4(jiti@2.7.0)) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.57.2(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.57.2(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.4(jiti@2.7.0)))(eslint@9.39.4(jiti@2.7.0)))(eslint@9.39.4(jiti@2.7.0)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.4(jiti@2.7.0)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.57.2(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.7.0)) eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.4(jiti@2.7.0)) eslint-plugin-react: 7.37.5(eslint@9.39.4(jiti@2.7.0)) eslint-plugin-react-hooks: 7.0.1(eslint@9.39.4(jiti@2.7.0)) @@ -5880,7 +5888,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.57.2(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.4(jiti@2.7.0)))(eslint@9.39.4(jiti@2.7.0)): + eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.4(jiti@2.7.0)): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.4.3 @@ -5891,22 +5899,22 @@ snapshots: tinyglobby: 0.2.15 unrs-resolver: 1.11.1 optionalDependencies: - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.57.2(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.57.2(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.4(jiti@2.7.0)))(eslint@9.39.4(jiti@2.7.0)))(eslint@9.39.4(jiti@2.7.0)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.57.2(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.7.0)) transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@8.57.2(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.57.2(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.4(jiti@2.7.0)))(eslint@9.39.4(jiti@2.7.0)))(eslint@9.39.4(jiti@2.7.0)): + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.57.2(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.7.0)): dependencies: debug: 3.2.7 optionalDependencies: '@typescript-eslint/parser': 8.57.2(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3) eslint: 9.39.4(jiti@2.7.0) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.57.2(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.4(jiti@2.7.0)))(eslint@9.39.4(jiti@2.7.0)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.4(jiti@2.7.0)) transitivePeerDependencies: - supports-color - eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.57.2(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.57.2(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.4(jiti@2.7.0)))(eslint@9.39.4(jiti@2.7.0)))(eslint@9.39.4(jiti@2.7.0)): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.57.2(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.7.0)): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -5917,7 +5925,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.39.4(jiti@2.7.0) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.57.2(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.57.2(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.4(jiti@2.7.0)))(eslint@9.39.4(jiti@2.7.0)))(eslint@9.39.4(jiti@2.7.0)) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.57.2(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.7.0)) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -6146,6 +6154,8 @@ snapshots: get-caller-file@2.0.5: {} + get-east-asian-width@1.6.0: {} + get-intrinsic@1.3.0: dependencies: call-bind-apply-helpers: 1.0.2 @@ -6306,8 +6316,6 @@ snapshots: dependencies: call-bound: 1.0.4 - is-fullwidth-code-point@3.0.0: {} - is-generator-function@1.1.2: dependencies: call-bound: 1.0.4 @@ -6823,8 +6831,6 @@ snapshots: gopd: 1.2.0 set-function-name: 2.0.2 - require-directory@2.1.1: {} - require-from-string@2.0.2: {} resolve-from@4.0.0: {} @@ -6974,7 +6980,7 @@ snapshots: shebang-regex@3.0.0: {} - shell-quote@1.8.3: {} + shell-quote@1.8.4: {} side-channel-list@1.0.0: dependencies: @@ -7021,11 +7027,11 @@ snapshots: es-errors: 1.3.0 internal-slot: 1.1.0 - string-width@4.2.3: + string-width@7.2.0: dependencies: - emoji-regex: 8.0.0 - is-fullwidth-code-point: 3.0.0 - strip-ansi: 6.0.1 + emoji-regex: 10.6.0 + get-east-asian-width: 1.6.0 + strip-ansi: 7.2.0 string.prototype.includes@2.0.1: dependencies: @@ -7077,9 +7083,9 @@ snapshots: define-properties: 1.2.1 es-object-atoms: 1.1.1 - strip-ansi@6.0.1: + strip-ansi@7.2.0: dependencies: - ansi-regex: 5.0.1 + ansi-regex: 6.2.2 strip-bom@3.0.0: {} @@ -7096,11 +7102,9 @@ snapshots: optionalDependencies: '@babel/core': 7.29.0 - supports-color@7.2.0: - dependencies: - has-flag: 4.0.0 + supports-color@10.2.2: {} - supports-color@8.1.1: + supports-color@7.2.0: dependencies: has-flag: 4.0.0 @@ -7415,11 +7419,11 @@ snapshots: word-wrap@1.2.5: {} - wrap-ansi@7.0.0: + wrap-ansi@9.0.2: dependencies: - ansi-styles: 4.3.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 + ansi-styles: 6.2.3 + string-width: 7.2.0 + strip-ansi: 7.2.0 xml-name-validator@5.0.0: {} @@ -7429,17 +7433,16 @@ snapshots: yallist@3.1.1: {} - yargs-parser@21.1.1: {} + yargs-parser@22.0.0: {} - yargs@17.7.2: + yargs@18.0.0: dependencies: - cliui: 8.0.1 + cliui: 9.0.1 escalade: 3.2.0 get-caller-file: 2.0.5 - require-directory: 2.1.1 - string-width: 4.2.3 + string-width: 7.2.0 y18n: 5.0.8 - yargs-parser: 21.1.1 + yargs-parser: 22.0.0 yocto-queue@0.1.0: {} From eb2928873e3d7536db9b4ba183becfd757723d61 Mon Sep 17 00:00:00 2001 From: Chris Kehayias Date: Sat, 30 May 2026 17:26:26 -0400 Subject: [PATCH 10/20] chore(env): document all env vars and remove NEXTAUTH fallbacks Audited the codebase for every env reference (process.env, import.meta.env, VITE_*, NEXT_PUBLIC_*) and updated .env.example so all required and optional variables are present with explanatory comments. Added notes for the OIDC -> MINISTRY_PLATFORM_CLIENT_* fallback, Vercel auto-detected origins, and the auto-derived NEXT_PUBLIC_APP_VERSION. Removed the legacy NEXTAUTH_URL / NEXTAUTH_SECRET fallbacks in favor of the canonical BETTER_AUTH_* names across auth, logout, the SDK Vite config, and the demo page. Co-Authored-By: Claude Opus 4.8 (1M context) --- .env.example | 21 +++++++++++++++++---- packages/embed-sdk/vite.config.ts | 2 +- src/app/(demo)/demo/[slug]/page.tsx | 2 +- src/app/api/auth/logout/route.ts | 2 +- src/lib/auth.ts | 4 ++-- 5 files changed, 22 insertions(+), 9 deletions(-) diff --git a/.env.example b/.env.example index 2b1dd74..ffc268d 100644 --- a/.env.example +++ b/.env.example @@ -6,21 +6,27 @@ BETTER_AUTH_URL=http://localhost:3000 BETTER_AUTH_SECRET=your-better-auth-secret-min-32-chars # ============================================================================ -# OIDC Provider (Ministry Platform OAuth) +# OIDC Provider (Ministry Platform OAuth — interactive user login) # ============================================================================ +# Credentials for the OAuth client used to sign users in. If unset, the code +# falls back to MINISTRY_PLATFORM_CLIENT_ID / _SECRET below (src/lib/auth.ts), +# so you only need these if your login client differs from the API client. OIDC_CLIENT_ID=your-oidc-client-id OIDC_CLIENT_SECRET=your-oidc-client-secret # ============================================================================ -# Ministry Platform API (service-to-service) +# Ministry Platform API (service-to-service / client credentials) # ============================================================================ +# Base URL must include the /ministryplatformapi suffix; server code strips it +# where a bare host is needed (src/lib/embed/config.ts). Required by the type +# generator (pnpm generate:types) and all MP-backed services. MINISTRY_PLATFORM_BASE_URL=https://your-mp-instance.com/ministryplatformapi MINISTRY_PLATFORM_CLIENT_ID=your-client-id MINISTRY_PLATFORM_CLIENT_SECRET=your-client-secret # Organization display name baked into the embed SDK at build time (e.g. the # SMS opt-in consent text in the profile widget). Unset falls back to a neutral -# phrase ("our organization"). +# phrase ("our organization"). Consumed by Vite at build (packages/embed-sdk). VITE_ORG_NAME= # ============================================================================ @@ -29,6 +35,10 @@ VITE_ORG_NAME= NEXT_PUBLIC_MINISTRY_PLATFORM_FILE_URL=https://your-mp-instance.com/ministryplatformapi/files NEXT_PUBLIC_APP_NAME=MPNext-Components +# NEXT_PUBLIC_APP_VERSION is NOT set here — it is derived automatically at build +# time from the repo's VERSION file (next.config.ts), defaulting to "dev". +# Setting it manually has no effect. + # ============================================================================ # Embed Widget Settings # ============================================================================ @@ -36,7 +46,10 @@ NEXT_PUBLIC_APP_NAME=MPNext-Components # Generate via: node -e "console.log(require('crypto').randomBytes(32).toString('base64url'))" EMBED_JWT_SECRET= -# Comma-separated origins allowed to call the embed API +# Comma-separated origins allowed to call the embed API. +# On Vercel, VERCEL_URL and VERCEL_PROJECT_PRODUCTION_URL (injected +# automatically by the platform) are also added to the allowlist — no need to +# set those yourself locally. EMBED_ALLOWED_ORIGINS=http://localhost:3000,http://localhost:5173 # ============================================================================ diff --git a/packages/embed-sdk/vite.config.ts b/packages/embed-sdk/vite.config.ts index d09d68e..3e63d52 100644 --- a/packages/embed-sdk/vite.config.ts +++ b/packages/embed-sdk/vite.config.ts @@ -13,7 +13,7 @@ export default defineConfig(({ mode }) => { // demo/widget surfaces a "not configured" state rather than a hardcoded host. const mpBaseUrl = (env.MINISTRY_PLATFORM_BASE_URL || "") .replace(/\/ministryplatformapi\/?$/, ""); - const apiHost = env.BETTER_AUTH_URL || env.NEXTAUTH_URL || "http://localhost:3000"; + const apiHost = env.BETTER_AUTH_URL || "http://localhost:3000"; // Organization display name baked into widgets (e.g. SMS opt-in consent text). // Tenant-configurable via VITE_ORG_NAME; empty falls back to a neutral phrase. diff --git a/src/app/(demo)/demo/[slug]/page.tsx b/src/app/(demo)/demo/[slug]/page.tsx index aeb38d4..64445c2 100644 --- a/src/app/(demo)/demo/[slug]/page.tsx +++ b/src/app/(demo)/demo/[slug]/page.tsx @@ -30,7 +30,7 @@ export default async function WidgetDemoPage({ notFound(); } - const apiHost = process.env.BETTER_AUTH_URL || process.env.NEXTAUTH_URL || "http://localhost:3000"; + const apiHost = process.env.BETTER_AUTH_URL || "http://localhost:3000"; // mp-base-url for widgets is the MP host without /ministryplatformapi path const mpBaseUrl = getMpHost(); diff --git a/src/app/api/auth/logout/route.ts b/src/app/api/auth/logout/route.ts index b325b6c..5004668 100644 --- a/src/app/api/auth/logout/route.ts +++ b/src/app/api/auth/logout/route.ts @@ -9,7 +9,7 @@ export async function POST(req: NextRequest) { const body = await req.json().catch(() => ({})) as { postLogoutRedirectUri?: string }; const postLogoutRedirectUri = - body.postLogoutRedirectUri || `${process.env.BETTER_AUTH_URL || process.env.NEXTAUTH_URL}/signin`; + body.postLogoutRedirectUri || `${process.env.BETTER_AUTH_URL}/signin`; await auth.api.signOut({ headers: hdrs }); diff --git a/src/lib/auth.ts b/src/lib/auth.ts index 5ed3d18..5768b7f 100644 --- a/src/lib/auth.ts +++ b/src/lib/auth.ts @@ -8,8 +8,8 @@ import { MPHelper } from "@/lib/providers/ministry-platform"; const mpBaseUrl = process.env.MINISTRY_PLATFORM_BASE_URL!; const options = { - baseURL: process.env.BETTER_AUTH_URL || process.env.NEXTAUTH_URL, - secret: process.env.BETTER_AUTH_SECRET || process.env.NEXTAUTH_SECRET, + baseURL: process.env.BETTER_AUTH_URL, + secret: process.env.BETTER_AUTH_SECRET, session: { cookieCache: { enabled: true, From c46ac75c43dcd913bef7e88bb99ff72d5a78c5ae Mon Sep 17 00:00:00 2001 From: Chris Kehayias Date: Sat, 30 May 2026 17:40:31 -0400 Subject: [PATCH 11/20] docs(readme): correct inaccurate claims after full-app review Align README with the actual implementation: drop the unimplemented multi-tenant/idempotency claims, fix the SDK loader description (static hashed loader, not an x-sdk-hash header), bump the Node requirement to 20.9+, remove the nonexistent src/contexts/ entry, add the logout/session-tokens routes and domainTimezoneService, fix the ProfileService example, and correct the Claude Code commands table. Co-Authored-By: Claude Opus 4.8 (1M context) --- README.md | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 59eba6c..61eca86 100644 --- a/README.md +++ b/README.md @@ -39,11 +39,11 @@ Embeddable Web Component widgets for [Ministry Platform](https://www.ministrypla - **Five embeddable widgets**: `next-user-menu`, `next-add-to-calendar`, `next-full-calendar`, `next-profile`, `next-my-invoices` — each a framework-agnostic Web Component rendered in Shadow DOM - **Framework-agnostic SDK**: Single `