Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| dafa5abab4 | |||
| 0dab318b66 | |||
| 932524125e | |||
| c291c88f2b | |||
| e42e4f5639 | |||
| b70fc88921 | |||
| 95aec8350a | |||
| 401228699f | |||
| 0d2b21199e |
+2
-1
@@ -37,7 +37,8 @@ LOG_LEVEL=warn
|
|||||||
# production: leave unset, or set OPENAPI_DOCS_ENABLED=false
|
# production: leave unset, or set OPENAPI_DOCS_ENABLED=false
|
||||||
# OPENAPI_DOCS_ENABLED=true
|
# OPENAPI_DOCS_ENABLED=true
|
||||||
|
|
||||||
# Timezone for scheduled reminders (e.g., Europe/Berlin, America/New_York)
|
# Server default timezone for scheduled reminders (e.g., Europe/Berlin, America/New_York).
|
||||||
|
# Users can override this per account in Settings -> Timezone.
|
||||||
TZ=Europe/Berlin
|
TZ=Europe/Berlin
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
|
|||||||
+12
-2
@@ -83,18 +83,28 @@ Thumbs.db
|
|||||||
AGENTS.md
|
AGENTS.md
|
||||||
docs/TECH_STACK.md
|
docs/TECH_STACK.md
|
||||||
doku/
|
doku/
|
||||||
|
|
||||||
|
# Local agent work logs stay on disk but must never go upstream.
|
||||||
doku/memory_notes.md
|
doku/memory_notes.md
|
||||||
doku/report.md
|
doku/report.md
|
||||||
plan/
|
plan/
|
||||||
.copilot-tracking/
|
.copilot-tracking/
|
||||||
.playwright-cli/
|
.playwright-cli/
|
||||||
|
.agents/
|
||||||
|
skills-lock.json
|
||||||
|
|
||||||
# ===================
|
# ===================
|
||||||
# Local Spec Kit artifacts
|
# Local Spec Kit workspace state
|
||||||
# ===================
|
# ===================
|
||||||
.specify/
|
.specify/
|
||||||
specs/
|
specs/
|
||||||
docs/SPEC_KIT.md
|
docs/SPEC_KIT.md
|
||||||
.github/agents/medassist-feature-orchestrator.agent.md
|
.github/agents/medassist-feature-orchestrator.agent.md
|
||||||
.github/agents/speckit.*.agent.md
|
.github/agents/speckit.*.agent.md
|
||||||
.github/prompts/speckit.*.prompt.md
|
.github/prompts/speckit.*.prompt.md
|
||||||
|
.github/skills/accessibility/
|
||||||
|
.github/skills/frontend-design/
|
||||||
|
.github/skills/nodejs-backend-patterns/
|
||||||
|
.github/skills/nodejs-best-practices/
|
||||||
|
.github/skills/seo/
|
||||||
|
.playwright-mcp
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="https://img.shields.io/badge/Backend_Tests-639%2F639-brightgreen?logo=vitest" alt="Backend Tests 454/454" />
|
<img src="https://img.shields.io/badge/Backend_Tests-640%2F640-brightgreen?logo=vitest" alt="Backend Tests 454/454" />
|
||||||
<img src="https://img.shields.io/badge/Frontend_Tests-884%2F884-brightgreen?logo=vitest" alt="Frontend Tests 611/611" />
|
<img src="https://img.shields.io/badge/Frontend_Tests-884%2F884-brightgreen?logo=vitest" alt="Frontend Tests 611/611" />
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
@@ -203,7 +203,7 @@ All configuration is done via environment variables in `.env`. Copy `.env.exampl
|
|||||||
| `CORS_ORIGINS` | `http://localhost:4174` | Allowed origins for CORS |
|
| `CORS_ORIGINS` | `http://localhost:4174` | Allowed origins for CORS |
|
||||||
| `LOG_LEVEL` | `info` | Log verbosity (`debug`, `info`, `warn`, `error`, `silent`). At `info` (default), high-frequency polling endpoints are suppressed. Set `debug` to see all requests. |
|
| `LOG_LEVEL` | `info` | Log verbosity (`debug`, `info`, `warn`, `error`, `silent`). At `info` (default), high-frequency polling endpoints are suppressed. Set `debug` to see all requests. |
|
||||||
| `OPENAPI_DOCS_ENABLED` | `auto` | Enables API docs in non-production by default. Set explicitly to `true`/`false` to override. |
|
| `OPENAPI_DOCS_ENABLED` | `auto` | Enables API docs in non-production by default. Set explicitly to `true`/`false` to override. |
|
||||||
| `TZ` | `Europe/Berlin` | Timezone for scheduled reminders |
|
| `TZ` | `Europe/Berlin` | Server default timezone for scheduled reminders (can be overridden per user in Settings) |
|
||||||
|
|
||||||
Recommended values for API docs by environment:
|
Recommended values for API docs by environment:
|
||||||
|
|
||||||
@@ -305,6 +305,8 @@ API reference:
|
|||||||
| `REMINDER_MINUTES_BEFORE` | `15` | Minutes before intake to send reminder |
|
| `REMINDER_MINUTES_BEFORE` | `15` | Minutes before intake to send reminder |
|
||||||
| `EXPIRY_WARNING_DAYS` | `30` | Days before expiry to show warning |
|
| `EXPIRY_WARNING_DAYS` | `30` | Days before expiry to show warning |
|
||||||
|
|
||||||
|
Intake reminder timing uses IANA timezones. The server uses `TZ` as default, and each user can set an override in Settings. If no user timezone is set, reminders continue using the server default.
|
||||||
|
|
||||||
### Push Notifications (Shoutrrr)
|
### Push Notifications (Shoutrrr)
|
||||||
|
|
||||||
MedAssist uses [Shoutrrr](https://containrrr.dev/shoutrrr/) for push notifications, supporting many services with a single URL format.
|
MedAssist uses [Shoutrrr](https://containrrr.dev/shoutrrr/) for push notifications, supporting many services with a single URL format.
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE `user_settings` ADD `timezone` text DEFAULT '' NOT NULL;
|
||||||
@@ -99,6 +99,13 @@
|
|||||||
"when": 1773348659979,
|
"when": 1773348659979,
|
||||||
"tag": "0013_add_share_medication_overview",
|
"tag": "0013_add_share_medication_overview",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 14,
|
||||||
|
"version": "6",
|
||||||
|
"when": 1775849300000,
|
||||||
|
"tag": "0014_add_user_settings_timezone",
|
||||||
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "medassist-ng-backend",
|
"name": "medassist-ng-backend",
|
||||||
"version": "1.22.2",
|
"version": "1.23.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ export async function runAlterMigrations(client: Client): Promise<{ success: boo
|
|||||||
`ALTER TABLE user_settings ADD COLUMN repeat_reminders_enabled integer NOT NULL DEFAULT 0`,
|
`ALTER TABLE user_settings ADD COLUMN repeat_reminders_enabled integer NOT NULL DEFAULT 0`,
|
||||||
`ALTER TABLE user_settings ADD COLUMN reminder_repeat_interval_minutes integer NOT NULL DEFAULT 30`,
|
`ALTER TABLE user_settings ADD COLUMN reminder_repeat_interval_minutes integer NOT NULL DEFAULT 30`,
|
||||||
`ALTER TABLE user_settings ADD COLUMN max_nagging_reminders integer NOT NULL DEFAULT 5`,
|
`ALTER TABLE user_settings ADD COLUMN max_nagging_reminders integer NOT NULL DEFAULT 5`,
|
||||||
|
`ALTER TABLE user_settings ADD COLUMN timezone text NOT NULL DEFAULT ''`,
|
||||||
`ALTER TABLE dose_tracking ADD COLUMN dismissed integer NOT NULL DEFAULT 0`,
|
`ALTER TABLE dose_tracking ADD COLUMN dismissed integer NOT NULL DEFAULT 0`,
|
||||||
`ALTER TABLE dose_tracking ADD COLUMN taken_source text NOT NULL DEFAULT 'manual'`,
|
`ALTER TABLE dose_tracking ADD COLUMN taken_source text NOT NULL DEFAULT 'manual'`,
|
||||||
`ALTER TABLE user_settings ADD COLUMN stock_calculation_mode text NOT NULL DEFAULT 'automatic'`,
|
`ALTER TABLE user_settings ADD COLUMN stock_calculation_mode text NOT NULL DEFAULT 'automatic'`,
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ export function getTableCreationSQL(): string[] {
|
|||||||
high_stock_days integer NOT NULL DEFAULT 180,
|
high_stock_days integer NOT NULL DEFAULT 180,
|
||||||
expiry_warning_days integer NOT NULL DEFAULT 90,
|
expiry_warning_days integer NOT NULL DEFAULT 90,
|
||||||
language text NOT NULL DEFAULT 'en',
|
language text NOT NULL DEFAULT 'en',
|
||||||
|
timezone text NOT NULL DEFAULT '',
|
||||||
stock_calculation_mode text NOT NULL DEFAULT 'automatic',
|
stock_calculation_mode text NOT NULL DEFAULT 'automatic',
|
||||||
share_stock_status integer NOT NULL DEFAULT 1,
|
share_stock_status integer NOT NULL DEFAULT 1,
|
||||||
upcoming_today_only integer NOT NULL DEFAULT 0,
|
upcoming_today_only integer NOT NULL DEFAULT 0,
|
||||||
|
|||||||
@@ -105,6 +105,7 @@ export const userSettings = sqliteTable("user_settings", {
|
|||||||
expiryWarningDays: integer("expiry_warning_days").notNull().default(90),
|
expiryWarningDays: integer("expiry_warning_days").notNull().default(90),
|
||||||
// UI preferences
|
// UI preferences
|
||||||
language: text("language", { length: 10 }).notNull().default("en"),
|
language: text("language", { length: 10 }).notNull().default("en"),
|
||||||
|
timezone: text("timezone", { length: 64 }).notNull().default(""),
|
||||||
// Stock calculation mode: "automatic" (schedule-based) or "manual" (only marked doses)
|
// Stock calculation mode: "automatic" (schedule-based) or "manual" (only marked doses)
|
||||||
stockCalculationMode: text("stock_calculation_mode", { length: 20 }).notNull().default("automatic"),
|
stockCalculationMode: text("stock_calculation_mode", { length: 20 }).notNull().default("automatic"),
|
||||||
// Whether shared schedule links show stock status (Critical/Low/Normal) to intake users
|
// Whether shared schedule links show stock status (Critical/Low/Normal) to intake users
|
||||||
|
|||||||
@@ -8,9 +8,11 @@ import { getSmtpConfig, sendEmailNotification } from "../services/notifications/
|
|||||||
import {
|
import {
|
||||||
classifyTestEmailFailure,
|
classifyTestEmailFailure,
|
||||||
getAllUserSettingsFromDb,
|
getAllUserSettingsFromDb,
|
||||||
|
getAvailableTimezones,
|
||||||
getDefaultSettings,
|
getDefaultSettings,
|
||||||
getNotificationProvider,
|
getNotificationProvider,
|
||||||
loadUserSettingsFromDb,
|
loadUserSettingsFromDb,
|
||||||
|
normalizeSettingsTimezone,
|
||||||
sanitizeNotificationUrl,
|
sanitizeNotificationUrl,
|
||||||
type UserSettings,
|
type UserSettings,
|
||||||
validateNotificationHostname,
|
validateNotificationHostname,
|
||||||
@@ -20,6 +22,7 @@ import type { AuthUser } from "../types/fastify.js";
|
|||||||
export type { UserSettings } from "../services/settings-service.js";
|
export type { UserSettings } from "../services/settings-service.js";
|
||||||
|
|
||||||
type SettingsBody = {
|
type SettingsBody = {
|
||||||
|
timezone: string;
|
||||||
emailEnabled: boolean;
|
emailEnabled: boolean;
|
||||||
notificationEmail: string;
|
notificationEmail: string;
|
||||||
reminderDaysBefore: number;
|
reminderDaysBefore: number;
|
||||||
@@ -174,6 +177,9 @@ export async function settingsRoutes(app: FastifyInstance) {
|
|||||||
const reminderMinutesBefore = envInt("REMINDER_MINUTES_BEFORE", 15);
|
const reminderMinutesBefore = envInt("REMINDER_MINUTES_BEFORE", 15);
|
||||||
|
|
||||||
return reply.send({
|
return reply.send({
|
||||||
|
timezone: settings.timezone ?? "",
|
||||||
|
availableTimezones: getAvailableTimezones(),
|
||||||
|
serverTimezone: process.env.TZ || "UTC",
|
||||||
// User notification settings (from DB)
|
// User notification settings (from DB)
|
||||||
emailEnabled: settings.emailEnabled,
|
emailEnabled: settings.emailEnabled,
|
||||||
notificationEmail: settings.notificationEmail ?? "",
|
notificationEmail: settings.notificationEmail ?? "",
|
||||||
@@ -241,6 +247,7 @@ export async function settingsRoutes(app: FastifyInstance) {
|
|||||||
type: "object",
|
type: "object",
|
||||||
required: ["emailEnabled", "notificationEmail", "reminderDaysBefore", "language"],
|
required: ["emailEnabled", "notificationEmail", "reminderDaysBefore", "language"],
|
||||||
properties: {
|
properties: {
|
||||||
|
timezone: { type: "string" },
|
||||||
emailEnabled: { type: "boolean" },
|
emailEnabled: { type: "boolean" },
|
||||||
notificationEmail: { type: "string" },
|
notificationEmail: { type: "string" },
|
||||||
reminderDaysBefore: { type: "number" },
|
reminderDaysBefore: { type: "number" },
|
||||||
@@ -293,6 +300,7 @@ export async function settingsRoutes(app: FastifyInstance) {
|
|||||||
upcomingTodayOnly: false,
|
upcomingTodayOnly: false,
|
||||||
shareScheduleTodayOnly: false,
|
shareScheduleTodayOnly: false,
|
||||||
swapDashboardMainSections: false,
|
swapDashboardMainSections: false,
|
||||||
|
timezone: "",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
response: {
|
response: {
|
||||||
@@ -318,6 +326,7 @@ export async function settingsRoutes(app: FastifyInstance) {
|
|||||||
const existingSettings = await db.select().from(userSettings).where(eq(userSettings.userId, userId));
|
const existingSettings = await db.select().from(userSettings).where(eq(userSettings.userId, userId));
|
||||||
|
|
||||||
const settingsData = {
|
const settingsData = {
|
||||||
|
timezone: normalizeSettingsTimezone(body.timezone),
|
||||||
emailEnabled: body.emailEnabled,
|
emailEnabled: body.emailEnabled,
|
||||||
notificationEmail: body.notificationEmail || null,
|
notificationEmail: body.notificationEmail || null,
|
||||||
emailStockReminders: body.emailStockReminders ?? true,
|
emailStockReminders: body.emailStockReminders ?? true,
|
||||||
|
|||||||
@@ -99,9 +99,16 @@ export function computeMedicationCurrentStock(options: {
|
|||||||
const match = doseIdPattern.exec(dose.doseId);
|
const match = doseIdPattern.exec(dose.doseId);
|
||||||
if (!match) continue;
|
if (!match) continue;
|
||||||
|
|
||||||
|
const parsedMedicationId = Number.parseInt(match[1], 10);
|
||||||
const parsedIntakeIndex = Number.parseInt(match[2], 10);
|
const parsedIntakeIndex = Number.parseInt(match[2], 10);
|
||||||
const doseDateOnlyMs = Number.parseInt(match[3], 10);
|
const doseDateOnlyMs = Number.parseInt(match[3], 10);
|
||||||
if (Number.isNaN(parsedIntakeIndex) || Number.isNaN(doseDateOnlyMs) || parsedIntakeIndex !== intakeIndex) {
|
if (
|
||||||
|
Number.isNaN(parsedMedicationId) ||
|
||||||
|
Number.isNaN(parsedIntakeIndex) ||
|
||||||
|
Number.isNaN(doseDateOnlyMs) ||
|
||||||
|
parsedMedicationId !== medication.id ||
|
||||||
|
parsedIntakeIndex !== intakeIndex
|
||||||
|
) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,9 +132,16 @@ export function computeMedicationCurrentStock(options: {
|
|||||||
const match = doseIdPattern.exec(dose.doseId);
|
const match = doseIdPattern.exec(dose.doseId);
|
||||||
if (!match) continue;
|
if (!match) continue;
|
||||||
|
|
||||||
|
const parsedMedicationId = Number.parseInt(match[1], 10);
|
||||||
const parsedIntakeIndex = Number.parseInt(match[2], 10);
|
const parsedIntakeIndex = Number.parseInt(match[2], 10);
|
||||||
const doseDateOnlyMs = Number.parseInt(match[3], 10);
|
const doseDateOnlyMs = Number.parseInt(match[3], 10);
|
||||||
if (Number.isNaN(parsedIntakeIndex) || Number.isNaN(doseDateOnlyMs) || parsedIntakeIndex !== intakeIndex) {
|
if (
|
||||||
|
Number.isNaN(parsedMedicationId) ||
|
||||||
|
Number.isNaN(parsedIntakeIndex) ||
|
||||||
|
Number.isNaN(doseDateOnlyMs) ||
|
||||||
|
parsedMedicationId !== medication.id ||
|
||||||
|
parsedIntakeIndex !== intakeIndex
|
||||||
|
) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import type { ServiceLogger } from "../utils/logger.js";
|
|||||||
import {
|
import {
|
||||||
cleanOldIntakeReminders,
|
cleanOldIntakeReminders,
|
||||||
createDefaultIntakeReminderState,
|
createDefaultIntakeReminderState,
|
||||||
getTimezone,
|
getEffectiveTimezone,
|
||||||
getTodaysIntakes,
|
getTodaysIntakes,
|
||||||
getUpcomingIntakes,
|
getUpcomingIntakes,
|
||||||
type IntakeReminderState,
|
type IntakeReminderState,
|
||||||
@@ -83,6 +83,16 @@ function formatIntakeLog(intake: {
|
|||||||
return `${intake.medName} (medId=${intake.medicationId}, intakeIndex=${intake.blisterIndex}, time=${intake.intakeTime.toISOString()}, localTime=${intake.intakeTimeStr}, usage=${intake.usage} ${doseUnit}, takenBy=${takenBy})`;
|
return `${intake.medName} (medId=${intake.medicationId}, intakeIndex=${intake.blisterIndex}, time=${intake.intakeTime.toISOString()}, localTime=${intake.intakeTimeStr}, usage=${intake.usage} ${doseUnit}, takenBy=${takenBy})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getMedicationDisplayName(med: { id: number; name: string | null; genericName: string | null }): string {
|
||||||
|
const commercialName = med.name?.trim() ?? "";
|
||||||
|
if (commercialName) return commercialName;
|
||||||
|
|
||||||
|
const genericName = med.genericName?.trim() ?? "";
|
||||||
|
if (genericName) return genericName;
|
||||||
|
|
||||||
|
return `Medication #${med.id}`;
|
||||||
|
}
|
||||||
|
|
||||||
async function autoMarkDueIntakesAsTaken(
|
async function autoMarkDueIntakesAsTaken(
|
||||||
settings: UserSettings & { userId: number },
|
settings: UserSettings & { userId: number },
|
||||||
rows: (typeof medications.$inferSelect)[],
|
rows: (typeof medications.$inferSelect)[],
|
||||||
@@ -137,7 +147,7 @@ async function autoMarkDueIntakesAsTaken(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const medicationTakenBy = parseTakenByJson(med.takenByJson);
|
const medicationTakenBy = parseTakenByJson(med.takenByJson);
|
||||||
const medDisplayName = med.name || med.genericName || "";
|
const medDisplayName = getMedicationDisplayName({ id: med.id, name: med.name, genericName: med.genericName });
|
||||||
let remainingStock = computeMedicationCurrentStock({
|
let remainingStock = computeMedicationCurrentStock({
|
||||||
medication: med,
|
medication: med,
|
||||||
doses: trackedDoses,
|
doses: trackedDoses,
|
||||||
@@ -425,7 +435,7 @@ export async function checkAndSendIntakeRemindersForUser(
|
|||||||
const activeRows = rows.filter((med) => med.isObsolete !== true).sort((left, right) => left.id - right.id);
|
const activeRows = rows.filter((med) => med.isObsolete !== true).sort((left, right) => left.id - right.id);
|
||||||
|
|
||||||
const locale = getDateLocale(language);
|
const locale = getDateLocale(language);
|
||||||
const tz = getTimezone();
|
const tz = getEffectiveTimezone(settings.timezone ?? null);
|
||||||
|
|
||||||
const autoMarkedCount = await autoMarkDueIntakesAsTaken(settings, activeRows, locale, tz, logger);
|
const autoMarkedCount = await autoMarkDueIntakesAsTaken(settings, activeRows, locale, tz, logger);
|
||||||
if (autoMarkedCount > 0) {
|
if (autoMarkedCount > 0) {
|
||||||
@@ -488,7 +498,7 @@ export async function checkAndSendIntakeRemindersForUser(
|
|||||||
for (const { med, intakes, intakesWithReminders } of reminderEntries) {
|
for (const { med, intakes, intakesWithReminders } of reminderEntries) {
|
||||||
// Medication-level takenBy (for fallback/display purposes)
|
// Medication-level takenBy (for fallback/display purposes)
|
||||||
const medicationTakenBy = parseTakenByJson(med.takenByJson);
|
const medicationTakenBy = parseTakenByJson(med.takenByJson);
|
||||||
const medDisplayName = med.name || med.genericName || "";
|
const medDisplayName = getMedicationDisplayName({ id: med.id, name: med.name, genericName: med.genericName });
|
||||||
|
|
||||||
// Process each intake separately to track blisterIndex
|
// Process each intake separately to track blisterIndex
|
||||||
intakesWithReminders.forEach((intake, _blisterIndex) => {
|
intakesWithReminders.forEach((intake, _blisterIndex) => {
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import {
|
|||||||
formatInTimezone,
|
formatInTimezone,
|
||||||
getCurrentHourInTimezone,
|
getCurrentHourInTimezone,
|
||||||
getDateOnlyTimestamp,
|
getDateOnlyTimestamp,
|
||||||
|
getEffectiveTimezone,
|
||||||
getMsUntilNextCheck,
|
getMsUntilNextCheck,
|
||||||
getNextScheduledOccurrenceTime,
|
getNextScheduledOccurrenceTime,
|
||||||
getNextScheduledTime,
|
getNextScheduledTime,
|
||||||
@@ -125,6 +126,16 @@ type PrescriptionReminderItem = {
|
|||||||
expiryDate: string | null;
|
expiryDate: string | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function getMedicationDisplayName(row: { id: number; name: string | null; genericName: string | null }): string {
|
||||||
|
const commercialName = row.name?.trim() ?? "";
|
||||||
|
if (commercialName) return commercialName;
|
||||||
|
|
||||||
|
const genericName = row.genericName?.trim() ?? "";
|
||||||
|
if (genericName) return genericName;
|
||||||
|
|
||||||
|
return `Medication #${row.id}`;
|
||||||
|
}
|
||||||
|
|
||||||
async function getMedicationsNeedingReminder(
|
async function getMedicationsNeedingReminder(
|
||||||
userId: number,
|
userId: number,
|
||||||
reminderDaysBefore: number,
|
reminderDaysBefore: number,
|
||||||
@@ -296,7 +307,7 @@ async function getMedicationsNeedingReminder(
|
|||||||
|
|
||||||
if (isCritical || isLow) {
|
if (isCritical || isLow) {
|
||||||
lowStock.push({
|
lowStock.push({
|
||||||
name: row.name,
|
name: getMedicationDisplayName({ id: row.id, name: row.name, genericName: row.genericName }),
|
||||||
medsLeft: currentPills,
|
medsLeft: currentPills,
|
||||||
daysLeft,
|
daysLeft,
|
||||||
depletionDate,
|
depletionDate,
|
||||||
@@ -322,7 +333,7 @@ async function getMedicationsNeedingPrescriptionReminder(userId: number): Promis
|
|||||||
(row.prescriptionRemainingRefills ?? 0) <= (row.prescriptionLowRefillThreshold ?? 1)
|
(row.prescriptionRemainingRefills ?? 0) <= (row.prescriptionLowRefillThreshold ?? 1)
|
||||||
)
|
)
|
||||||
.map((row) => ({
|
.map((row) => ({
|
||||||
name: row.name,
|
name: getMedicationDisplayName({ id: row.id, name: row.name, genericName: row.genericName }),
|
||||||
remainingRefills: row.prescriptionRemainingRefills ?? 0,
|
remainingRefills: row.prescriptionRemainingRefills ?? 0,
|
||||||
lowThreshold: row.prescriptionLowRefillThreshold ?? 1,
|
lowThreshold: row.prescriptionLowRefillThreshold ?? 1,
|
||||||
expiryDate: row.prescriptionExpiryDate ?? null,
|
expiryDate: row.prescriptionExpiryDate ?? null,
|
||||||
@@ -534,7 +545,8 @@ async function checkAndSendReminderForUser(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const state = loadReminderState();
|
const state = loadReminderState();
|
||||||
const today = getTodayInTimezone(); // YYYY-MM-DD in configured timezone
|
const userTimezone = getEffectiveTimezone(settings.timezone ?? null);
|
||||||
|
const today = getTodayInTimezone(userTimezone); // YYYY-MM-DD in effective user timezone
|
||||||
const userStateKey = `user_${settings.userId}`;
|
const userStateKey = `user_${settings.userId}`;
|
||||||
const userStockNotifiedKey = `${userStateKey}_${today}_stock`;
|
const userStockNotifiedKey = `${userStateKey}_${today}_stock`;
|
||||||
const userPrescriptionNotifiedKey = `${userStateKey}_${today}_prescription`;
|
const userPrescriptionNotifiedKey = `${userStateKey}_${today}_prescription`;
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import type { Language } from "../i18n/translations.js";
|
|||||||
|
|
||||||
export type UserSettings = {
|
export type UserSettings = {
|
||||||
userId: number;
|
userId: number;
|
||||||
|
timezone?: string | null;
|
||||||
emailEnabled: boolean;
|
emailEnabled: boolean;
|
||||||
notificationEmail: string | null;
|
notificationEmail: string | null;
|
||||||
emailStockReminders: boolean;
|
emailStockReminders: boolean;
|
||||||
@@ -105,6 +106,7 @@ function envInt(key: string, defaultVal: number): number {
|
|||||||
|
|
||||||
export function getDefaultSettings() {
|
export function getDefaultSettings() {
|
||||||
return {
|
return {
|
||||||
|
timezone: "",
|
||||||
emailEnabled: envBool("DEFAULT_EMAIL_ENABLED", false),
|
emailEnabled: envBool("DEFAULT_EMAIL_ENABLED", false),
|
||||||
notificationEmail: process.env.DEFAULT_NOTIFICATION_EMAIL || null,
|
notificationEmail: process.env.DEFAULT_NOTIFICATION_EMAIL || null,
|
||||||
emailStockReminders: envBool("DEFAULT_EMAIL_STOCK_REMINDERS", true),
|
emailStockReminders: envBool("DEFAULT_EMAIL_STOCK_REMINDERS", true),
|
||||||
@@ -144,6 +146,33 @@ export function getDefaultSettings() {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type IntlWithSupportedValuesOf = typeof Intl & {
|
||||||
|
supportedValuesOf?: (key: string) => string[];
|
||||||
|
};
|
||||||
|
|
||||||
|
let cachedTimezones: Set<string> | null = null;
|
||||||
|
|
||||||
|
function getTimezoneSet(): Set<string> {
|
||||||
|
if (cachedTimezones) return cachedTimezones;
|
||||||
|
const intlWithSupportedValues = Intl as IntlWithSupportedValuesOf;
|
||||||
|
if (typeof intlWithSupportedValues.supportedValuesOf === "function") {
|
||||||
|
cachedTimezones = new Set(intlWithSupportedValues.supportedValuesOf("timeZone"));
|
||||||
|
return cachedTimezones;
|
||||||
|
}
|
||||||
|
cachedTimezones = new Set([process.env.TZ || "UTC", "UTC"]);
|
||||||
|
return cachedTimezones;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getAvailableTimezones(): string[] {
|
||||||
|
return [...getTimezoneSet()].sort((left, right) => left.localeCompare(right));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function normalizeSettingsTimezone(value: string | null | undefined): string {
|
||||||
|
const trimmed = value?.trim() ?? "";
|
||||||
|
if (!trimmed) return "";
|
||||||
|
return getTimezoneSet().has(trimmed) ? trimmed : "";
|
||||||
|
}
|
||||||
|
|
||||||
export function validateNotificationHostname(hostnameRaw: string): string | null {
|
export function validateNotificationHostname(hostnameRaw: string): string | null {
|
||||||
const hostname = hostnameRaw.toLowerCase();
|
const hostname = hostnameRaw.toLowerCase();
|
||||||
|
|
||||||
@@ -245,6 +274,7 @@ export async function loadUserSettingsFromDb(userId: number): Promise<UserSettin
|
|||||||
const settings = await getOrCreateUserSettings(userId);
|
const settings = await getOrCreateUserSettings(userId);
|
||||||
return {
|
return {
|
||||||
userId: settings.userId,
|
userId: settings.userId,
|
||||||
|
timezone: settings.timezone?.trim() ? settings.timezone : null,
|
||||||
emailEnabled: settings.emailEnabled,
|
emailEnabled: settings.emailEnabled,
|
||||||
notificationEmail: settings.notificationEmail,
|
notificationEmail: settings.notificationEmail,
|
||||||
emailStockReminders: settings.emailStockReminders,
|
emailStockReminders: settings.emailStockReminders,
|
||||||
@@ -288,6 +318,7 @@ export async function getAllUserSettingsFromDb(): Promise<UserSettings[]> {
|
|||||||
const allSettings = await db.select().from(userSettings);
|
const allSettings = await db.select().from(userSettings);
|
||||||
return allSettings.map((settings) => ({
|
return allSettings.map((settings) => ({
|
||||||
userId: settings.userId,
|
userId: settings.userId,
|
||||||
|
timezone: settings.timezone?.trim() ? settings.timezone : null,
|
||||||
emailEnabled: settings.emailEnabled,
|
emailEnabled: settings.emailEnabled,
|
||||||
notificationEmail: settings.notificationEmail,
|
notificationEmail: settings.notificationEmail,
|
||||||
emailStockReminders: settings.emailStockReminders,
|
emailStockReminders: settings.emailStockReminders,
|
||||||
|
|||||||
@@ -123,6 +123,7 @@ async function createSchema(client: Client) {
|
|||||||
`CREATE TABLE IF NOT EXISTS user_settings (
|
`CREATE TABLE IF NOT EXISTS user_settings (
|
||||||
id integer PRIMARY KEY AUTOINCREMENT,
|
id integer PRIMARY KEY AUTOINCREMENT,
|
||||||
user_id integer NOT NULL UNIQUE,
|
user_id integer NOT NULL UNIQUE,
|
||||||
|
timezone text NOT NULL DEFAULT '',
|
||||||
email_enabled integer NOT NULL DEFAULT 0,
|
email_enabled integer NOT NULL DEFAULT 0,
|
||||||
notification_email text,
|
notification_email text,
|
||||||
email_stock_reminders integer NOT NULL DEFAULT 1,
|
email_stock_reminders integer NOT NULL DEFAULT 1,
|
||||||
|
|||||||
@@ -117,6 +117,7 @@ async function createSchema(client: Client) {
|
|||||||
`CREATE TABLE IF NOT EXISTS user_settings (
|
`CREATE TABLE IF NOT EXISTS user_settings (
|
||||||
id integer PRIMARY KEY AUTOINCREMENT,
|
id integer PRIMARY KEY AUTOINCREMENT,
|
||||||
user_id integer NOT NULL UNIQUE,
|
user_id integer NOT NULL UNIQUE,
|
||||||
|
timezone text NOT NULL DEFAULT '',
|
||||||
email_enabled integer NOT NULL DEFAULT 0,
|
email_enabled integer NOT NULL DEFAULT 0,
|
||||||
notification_email text,
|
notification_email text,
|
||||||
email_stock_reminders integer NOT NULL DEFAULT 1,
|
email_stock_reminders integer NOT NULL DEFAULT 1,
|
||||||
|
|||||||
@@ -134,6 +134,7 @@ async function createSchema(client: Client) {
|
|||||||
`CREATE TABLE IF NOT EXISTS user_settings (
|
`CREATE TABLE IF NOT EXISTS user_settings (
|
||||||
id integer PRIMARY KEY AUTOINCREMENT,
|
id integer PRIMARY KEY AUTOINCREMENT,
|
||||||
user_id integer NOT NULL UNIQUE,
|
user_id integer NOT NULL UNIQUE,
|
||||||
|
timezone text NOT NULL DEFAULT '',
|
||||||
email_enabled integer NOT NULL DEFAULT 0,
|
email_enabled integer NOT NULL DEFAULT 0,
|
||||||
notification_email text,
|
notification_email text,
|
||||||
email_stock_reminders integer NOT NULL DEFAULT 1,
|
email_stock_reminders integer NOT NULL DEFAULT 1,
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ async function setStockMode(mode: "automatic" | "manual") {
|
|||||||
|
|
||||||
async function createMedication(options: {
|
async function createMedication(options: {
|
||||||
name: string;
|
name: string;
|
||||||
|
genericName?: string | null;
|
||||||
packCount?: number;
|
packCount?: number;
|
||||||
blistersPerPack?: number;
|
blistersPerPack?: number;
|
||||||
pillsPerBlister?: number;
|
pillsPerBlister?: number;
|
||||||
@@ -80,6 +81,7 @@ async function createMedication(options: {
|
|||||||
}) {
|
}) {
|
||||||
const {
|
const {
|
||||||
name,
|
name,
|
||||||
|
genericName = null,
|
||||||
packCount = 1,
|
packCount = 1,
|
||||||
blistersPerPack = 1,
|
blistersPerPack = 1,
|
||||||
pillsPerBlister = 10,
|
pillsPerBlister = 10,
|
||||||
@@ -106,16 +108,17 @@ async function createMedication(options: {
|
|||||||
|
|
||||||
const result = await testClient.execute({
|
const result = await testClient.execute({
|
||||||
sql: `INSERT INTO medications (
|
sql: `INSERT INTO medications (
|
||||||
user_id, name, taken_by_json, package_type,
|
user_id, name, generic_name, taken_by_json, package_type,
|
||||||
pack_count, blisters_per_pack, pills_per_blister, loose_tablets,
|
pack_count, blisters_per_pack, pills_per_blister, loose_tablets,
|
||||||
stock_adjustment, last_stock_correction_at,
|
stock_adjustment, last_stock_correction_at,
|
||||||
usage_json, every_json, start_json, intakes_json,
|
usage_json, every_json, start_json, intakes_json,
|
||||||
is_obsolete, intake_reminders_enabled
|
is_obsolete, intake_reminders_enabled
|
||||||
) VALUES (?, ?, ?, 'blister', ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0)
|
) VALUES (?, ?, ?, ?, 'blister', ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0)
|
||||||
RETURNING id`,
|
RETURNING id`,
|
||||||
args: [
|
args: [
|
||||||
1,
|
1,
|
||||||
name,
|
name,
|
||||||
|
genericName,
|
||||||
JSON.stringify(takenBy),
|
JSON.stringify(takenBy),
|
||||||
packCount,
|
packCount,
|
||||||
blistersPerPack,
|
blistersPerPack,
|
||||||
@@ -348,6 +351,21 @@ describe("Stock semantics parity (planner usage vs scheduler)", () => {
|
|||||||
const lowStock = await getMedicationsNeedingReminderForTests(1, 7, 365, "en", "automatic");
|
const lowStock = await getMedicationsNeedingReminderForTests(1, 7, 365, "en", "automatic");
|
||||||
expect(lowStock.some((r) => r.name === "Obsolete Med")).toBe(false);
|
expect(lowStock.some((r) => r.name === "Obsolete Med")).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("uses generic name fallback in scheduler reminders when commercial name is empty", async () => {
|
||||||
|
await setStockMode("automatic");
|
||||||
|
await createMedication({
|
||||||
|
name: "",
|
||||||
|
genericName: "Acetylsalicylic acid",
|
||||||
|
packCount: 1,
|
||||||
|
blistersPerPack: 1,
|
||||||
|
pillsPerBlister: 10,
|
||||||
|
intakes: [{ usage: 1, every: 1, start: "2026-01-01T08:00:00" }],
|
||||||
|
});
|
||||||
|
|
||||||
|
const lowStock = await getMedicationsNeedingReminderForTests(1, 7, 365, "en", "automatic");
|
||||||
|
expect(lowStock.some((r) => r.name === "Acetylsalicylic acid")).toBe(true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("getLiquidReminderThresholds", () => {
|
describe("getLiquidReminderThresholds", () => {
|
||||||
|
|||||||
@@ -64,6 +64,16 @@ function toDateOnly(date: Date): Date {
|
|||||||
return new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0, 0);
|
return new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getLocalDateOrdinal(date: Date): number {
|
||||||
|
return Math.floor(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()) / 86_400_000);
|
||||||
|
}
|
||||||
|
|
||||||
|
function addLocalCalendarDays(date: Date, days: number): Date {
|
||||||
|
const next = new Date(date);
|
||||||
|
next.setDate(next.getDate() + days);
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
export function getDateOnlyTimestamp(date: Date): number {
|
export function getDateOnlyTimestamp(date: Date): number {
|
||||||
return toDateOnly(date).getTime();
|
return toDateOnly(date).getTime();
|
||||||
}
|
}
|
||||||
@@ -175,13 +185,23 @@ export function getNextScheduledOccurrenceTime(
|
|||||||
|
|
||||||
const lowerBound = inclusive ? fromMs : fromMs + 1;
|
const lowerBound = inclusive ? fromMs : fromMs + 1;
|
||||||
if (schedule.scheduleMode !== "weekdays") {
|
if (schedule.scheduleMode !== "weekdays") {
|
||||||
const period = Math.max(1, schedule.every) * 86_400_000;
|
const intervalDays = Math.max(1, schedule.every);
|
||||||
if (startTime >= lowerBound) {
|
if (startTime >= lowerBound) {
|
||||||
return startTime;
|
return startTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
const intervals = Math.ceil((lowerBound - startTime) / period);
|
const lowerBoundDate = new Date(lowerBound);
|
||||||
return startTime + intervals * period;
|
const startOrdinal = getLocalDateOrdinal(startDate);
|
||||||
|
const lowerBoundOrdinal = getLocalDateOrdinal(lowerBoundDate);
|
||||||
|
const daysBetween = Math.max(0, lowerBoundOrdinal - startOrdinal);
|
||||||
|
const wholeIntervals = Math.floor(daysBetween / intervalDays);
|
||||||
|
|
||||||
|
let candidate = addLocalCalendarDays(startDate, wholeIntervals * intervalDays);
|
||||||
|
while (candidate.getTime() < lowerBound) {
|
||||||
|
candidate = addLocalCalendarDays(candidate, intervalDays);
|
||||||
|
}
|
||||||
|
|
||||||
|
return candidate.getTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
const candidateStart = Math.max(lowerBound, startTime);
|
const candidateStart = Math.max(lowerBound, startTime);
|
||||||
@@ -224,17 +244,28 @@ export function forEachScheduledOccurrenceInRange(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (schedule.scheduleMode !== "weekdays") {
|
if (schedule.scheduleMode !== "weekdays") {
|
||||||
const period = Math.max(1, schedule.every) * 86_400_000;
|
const intervalDays = Math.max(1, schedule.every);
|
||||||
let occurrenceMs = startTime;
|
let occurrence = new Date(startDate);
|
||||||
if (occurrenceMs < rangeStartMs) {
|
if (occurrence.getTime() < rangeStartMs) {
|
||||||
const intervals = Math.ceil((rangeStartMs - occurrenceMs) / period);
|
const rangeStartDate = new Date(rangeStartMs);
|
||||||
occurrenceMs += intervals * period;
|
const startOrdinal = getLocalDateOrdinal(startDate);
|
||||||
|
const rangeStartOrdinal = getLocalDateOrdinal(rangeStartDate);
|
||||||
|
const daysBetween = Math.max(0, rangeStartOrdinal - startOrdinal);
|
||||||
|
const wholeIntervals = Math.floor(daysBetween / intervalDays);
|
||||||
|
occurrence = addLocalCalendarDays(startDate, wholeIntervals * intervalDays);
|
||||||
|
|
||||||
|
while (occurrence.getTime() < rangeStartMs) {
|
||||||
|
occurrence = addLocalCalendarDays(occurrence, intervalDays);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (; occurrenceMs <= rangeEndMs; occurrenceMs += period) {
|
for (let occurrenceMs = occurrence.getTime(); occurrenceMs <= rangeEndMs; ) {
|
||||||
if (occurrenceMs >= rangeStartMs) {
|
if (occurrenceMs >= rangeStartMs) {
|
||||||
callback(occurrenceMs);
|
callback(occurrenceMs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
occurrence = addLocalCalendarDays(occurrence, intervalDays);
|
||||||
|
occurrenceMs = occurrence.getTime();
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -348,6 +379,23 @@ export function getTimezone(): string {
|
|||||||
return process.env.TZ || "UTC";
|
return process.env.TZ || "UTC";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isValidTimezone(value: string): boolean {
|
||||||
|
try {
|
||||||
|
new Intl.DateTimeFormat("en-US", { timeZone: value });
|
||||||
|
return true;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getEffectiveTimezone(override?: string | null): string {
|
||||||
|
const normalized = override?.trim() ?? "";
|
||||||
|
if (normalized && isValidTimezone(normalized)) {
|
||||||
|
return normalized;
|
||||||
|
}
|
||||||
|
return getTimezone();
|
||||||
|
}
|
||||||
|
|
||||||
/** Format a date in the configured timezone */
|
/** Format a date in the configured timezone */
|
||||||
export function formatInTimezone(date: Date, tz?: string): string {
|
export function formatInTimezone(date: Date, tz?: string): string {
|
||||||
return date.toLocaleString("de-DE", {
|
return date.toLocaleString("de-DE", {
|
||||||
|
|||||||
Generated
+236
-231
@@ -1,37 +1,37 @@
|
|||||||
{
|
{
|
||||||
"name": "medassist-ng-frontend",
|
"name": "medassist-ng-frontend",
|
||||||
"version": "1.22.1",
|
"version": "1.23.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "medassist-ng-frontend",
|
"name": "medassist-ng-frontend",
|
||||||
"version": "1.22.1",
|
"version": "1.23.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"i18next": "^26.0.3",
|
"i18next": "^26.0.4",
|
||||||
"i18next-browser-languagedetector": "^8.2.1",
|
"i18next-browser-languagedetector": "^8.2.1",
|
||||||
"lucide-react": "^1.7.0",
|
"lucide-react": "^1.8.0",
|
||||||
"react": "^19.2.4",
|
"react": "^19.2.5",
|
||||||
"react-dom": "^19.2.4",
|
"react-dom": "^19.2.5",
|
||||||
"react-i18next": "^17.0.2",
|
"react-i18next": "^17.0.2",
|
||||||
"react-router-dom": "^7.14.0",
|
"react-router-dom": "^7.14.0",
|
||||||
"zod": "^4.3.6"
|
"zod": "^4.3.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@biomejs/biome": "^2.4.10",
|
"@biomejs/biome": "^2.4.11",
|
||||||
"@playwright/test": "^1.59.1",
|
"@playwright/test": "^1.59.1",
|
||||||
"@testing-library/jest-dom": "^6.9.1",
|
"@testing-library/jest-dom": "^6.9.1",
|
||||||
"@testing-library/react": "^16.3.2",
|
"@testing-library/react": "^16.3.2",
|
||||||
"@testing-library/user-event": "^14.6.1",
|
"@testing-library/user-event": "^14.6.1",
|
||||||
"@types/node": "^25.5.2",
|
"@types/node": "^25.6.0",
|
||||||
"@types/react": "^19.2.14",
|
"@types/react": "^19.2.14",
|
||||||
"@types/react-dom": "^19.2.3",
|
"@types/react-dom": "^19.2.3",
|
||||||
"@types/react-router-dom": "^5.3.3",
|
"@types/react-router-dom": "^5.3.3",
|
||||||
"@vitejs/plugin-react": "^6.0.1",
|
"@vitejs/plugin-react": "^6.0.1",
|
||||||
"@vitest/coverage-v8": "^4.1.2",
|
"@vitest/coverage-v8": "^4.1.4",
|
||||||
"jsdom": "^29.0.1",
|
"jsdom": "^29.0.2",
|
||||||
"typescript": "^6.0.2",
|
"typescript": "^6.0.2",
|
||||||
"vite": "^8.0.5",
|
"vite": "^8.0.8",
|
||||||
"vitest": "^4.1.0"
|
"vitest": "^4.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -43,34 +43,32 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@asamuzakjp/css-color": {
|
"node_modules/@asamuzakjp/css-color": {
|
||||||
"version": "5.0.1",
|
"version": "5.1.10",
|
||||||
"resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-5.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-5.1.10.tgz",
|
||||||
"integrity": "sha512-2SZFvqMyvboVV1d15lMf7XiI3m7SDqXUuKaTymJYLN6dSGadqp+fVojqJlVoMlbZnlTmu3S0TLwLTJpvBMO1Aw==",
|
"integrity": "sha512-02OhhkKtgNRuicQ/nF3TRnGsxL9wp0r3Y7VlKWyOHHGmGyvXv03y+PnymU8FKFJMTjIr1Bk8U2g1HWSLrpAHww==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@csstools/css-calc": "^3.1.1",
|
"@csstools/css-calc": "^3.1.1",
|
||||||
"@csstools/css-color-parser": "^4.0.2",
|
"@csstools/css-color-parser": "^4.0.2",
|
||||||
"@csstools/css-parser-algorithms": "^4.0.0",
|
"@csstools/css-parser-algorithms": "^4.0.0",
|
||||||
"@csstools/css-tokenizer": "^4.0.0",
|
"@csstools/css-tokenizer": "^4.0.0"
|
||||||
"lru-cache": "^11.2.6"
|
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
|
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@asamuzakjp/dom-selector": {
|
"node_modules/@asamuzakjp/dom-selector": {
|
||||||
"version": "7.0.3",
|
"version": "7.0.9",
|
||||||
"resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-7.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-7.0.9.tgz",
|
||||||
"integrity": "sha512-Q6mU0Z6bfj6YvnX2k9n0JxiIwrCFN59x/nWmYQnAqP000ruX/yV+5bp/GRcF5T8ncvfwJQ7fgfP74DlpKExILA==",
|
"integrity": "sha512-r3ElRr7y8ucyN2KdICwGsmj19RoN13CLCa/pvGydghWK6ZzeKQ+TcDjVdtEZz2ElpndM5jXw//B9CEee0mWnVg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@asamuzakjp/nwsapi": "^2.3.9",
|
"@asamuzakjp/nwsapi": "^2.3.9",
|
||||||
"bidi-js": "^1.0.3",
|
"bidi-js": "^1.0.3",
|
||||||
"css-tree": "^3.2.1",
|
"css-tree": "^3.2.1",
|
||||||
"is-potential-custom-element-name": "^1.0.1",
|
"is-potential-custom-element-name": "^1.0.1"
|
||||||
"lru-cache": "^11.2.7"
|
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
|
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
|
||||||
@@ -169,9 +167,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@biomejs/biome": {
|
"node_modules/@biomejs/biome": {
|
||||||
"version": "2.4.10",
|
"version": "2.4.11",
|
||||||
"resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.4.10.tgz",
|
"resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.4.11.tgz",
|
||||||
"integrity": "sha512-xxA3AphFQ1geij4JTHXv4EeSTda1IFn22ye9LdyVPoJU19fNVl0uzfEuhsfQ4Yue/0FaLs2/ccVi4UDiE7R30w==",
|
"integrity": "sha512-nWxHX8tf3Opb/qRgZpBbsTOqOodkbrkJ7S+JxJAruxOReaDPPmPuLBAGQ8vigyUgo0QBB+oQltNEAvalLcjggA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT OR Apache-2.0",
|
"license": "MIT OR Apache-2.0",
|
||||||
"bin": {
|
"bin": {
|
||||||
@@ -185,20 +183,20 @@
|
|||||||
"url": "https://opencollective.com/biome"
|
"url": "https://opencollective.com/biome"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@biomejs/cli-darwin-arm64": "2.4.10",
|
"@biomejs/cli-darwin-arm64": "2.4.11",
|
||||||
"@biomejs/cli-darwin-x64": "2.4.10",
|
"@biomejs/cli-darwin-x64": "2.4.11",
|
||||||
"@biomejs/cli-linux-arm64": "2.4.10",
|
"@biomejs/cli-linux-arm64": "2.4.11",
|
||||||
"@biomejs/cli-linux-arm64-musl": "2.4.10",
|
"@biomejs/cli-linux-arm64-musl": "2.4.11",
|
||||||
"@biomejs/cli-linux-x64": "2.4.10",
|
"@biomejs/cli-linux-x64": "2.4.11",
|
||||||
"@biomejs/cli-linux-x64-musl": "2.4.10",
|
"@biomejs/cli-linux-x64-musl": "2.4.11",
|
||||||
"@biomejs/cli-win32-arm64": "2.4.10",
|
"@biomejs/cli-win32-arm64": "2.4.11",
|
||||||
"@biomejs/cli-win32-x64": "2.4.10"
|
"@biomejs/cli-win32-x64": "2.4.11"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@biomejs/cli-darwin-arm64": {
|
"node_modules/@biomejs/cli-darwin-arm64": {
|
||||||
"version": "2.4.10",
|
"version": "2.4.11",
|
||||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.4.10.tgz",
|
"resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.4.11.tgz",
|
||||||
"integrity": "sha512-vuzzI1cWqDVzOMIkYyHbKqp+AkQq4K7k+UCXWpkYcY/HDn1UxdsbsfgtVpa40shem8Kax4TLDLlx8kMAecgqiw==",
|
"integrity": "sha512-wOt+ed+L2dgZanWyL6i29qlXMc088N11optzpo10peayObBaAshbTcxKUchzEMp9QSY8rh5h6VfAFE3WTS1rqg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -213,9 +211,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@biomejs/cli-darwin-x64": {
|
"node_modules/@biomejs/cli-darwin-x64": {
|
||||||
"version": "2.4.10",
|
"version": "2.4.11",
|
||||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.4.10.tgz",
|
"resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.4.11.tgz",
|
||||||
"integrity": "sha512-14fzASRo+BPotwp7nWULy2W5xeUyFnTaq1V13Etrrxkrih+ez/2QfgFm5Ehtf5vSjtgx/IJycMMpn5kPd5ZNaA==",
|
"integrity": "sha512-gZ6zR8XmZlExfi/Pz/PffmdpWOQ8Qhy7oBztgkR8/ylSRyLwfRPSadmiVCV8WQ8PoJ2MWUy2fgID9zmtgUUJmw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -230,9 +228,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@biomejs/cli-linux-arm64": {
|
"node_modules/@biomejs/cli-linux-arm64": {
|
||||||
"version": "2.4.10",
|
"version": "2.4.11",
|
||||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.4.10.tgz",
|
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.4.11.tgz",
|
||||||
"integrity": "sha512-7MH1CMW5uuxQ/s7FLST63qF8B3Hgu2HRdZ7tA1X1+mk+St4JOuIrqdhIBnnyqeyWJNI+Bww7Es5QZ0wIc1Cmkw==",
|
"integrity": "sha512-avdJaEElXrKceK0va9FkJ4P5ci3N01TGkc6ni3P8l3BElqbOz42Wg2IyX3gbh0ZLEd4HVKEIrmuVu/AMuSeFFA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -247,9 +245,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@biomejs/cli-linux-arm64-musl": {
|
"node_modules/@biomejs/cli-linux-arm64-musl": {
|
||||||
"version": "2.4.10",
|
"version": "2.4.11",
|
||||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.4.10.tgz",
|
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.4.11.tgz",
|
||||||
"integrity": "sha512-WrJY6UuiSD/Dh+nwK2qOTu8kdMDlLV3dLMmychIghHPAysWFq1/DGC1pVZx8POE3ZkzKR3PUUnVrtZfMfaJjyQ==",
|
"integrity": "sha512-+Sbo1OAmlegtdwqFE8iOxFIWLh1B3OEgsuZfBpyyN/kWuqZ8dx9ZEes6zVnDMo+zRHF2wLynRVhoQmV7ohxl2Q==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -264,9 +262,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@biomejs/cli-linux-x64": {
|
"node_modules/@biomejs/cli-linux-x64": {
|
||||||
"version": "2.4.10",
|
"version": "2.4.11",
|
||||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.4.10.tgz",
|
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.4.11.tgz",
|
||||||
"integrity": "sha512-tZLvEEi2u9Xu1zAqRjTcpIDGVtldigVvzug2fTuPG0ME/g8/mXpRPcNgLB22bGn6FvLJpHHnqLnwliOu8xjYrg==",
|
"integrity": "sha512-TagWV0iomp5LnEnxWFg4nQO+e52Fow349vaX0Q/PIcX6Zhk4GGBgp3qqZ8PVkpC+cuehRctMf3+6+FgQ8jCEFQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -281,9 +279,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@biomejs/cli-linux-x64-musl": {
|
"node_modules/@biomejs/cli-linux-x64-musl": {
|
||||||
"version": "2.4.10",
|
"version": "2.4.11",
|
||||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.4.10.tgz",
|
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.4.11.tgz",
|
||||||
"integrity": "sha512-kDTi3pI6PBN6CiczsWYOyP2zk0IJI08EWEQyDMQWW221rPaaEz6FvjLhnU07KMzLv8q3qSuoB93ua6inSQ55Tw==",
|
"integrity": "sha512-bexd2IklK7ZgPhrz6jXzpIL6dEAH9MlJU1xGTrypx+FICxrXUp4CqtwfiuoDKse+UlgAlWtzML3jrMqeEAHEhA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -298,9 +296,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@biomejs/cli-win32-arm64": {
|
"node_modules/@biomejs/cli-win32-arm64": {
|
||||||
"version": "2.4.10",
|
"version": "2.4.11",
|
||||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.4.10.tgz",
|
"resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.4.11.tgz",
|
||||||
"integrity": "sha512-umwQU6qPzH+ISTf/eHyJ/QoQnJs3V9Vpjz2OjZXe9MVBZ7prgGafMy7yYeRGnlmDAn87AKTF3Q6weLoMGpeqdQ==",
|
"integrity": "sha512-RJhaTnY8byzxDt4bDVb7AFPHkPcjOPK3xBip4ZRTrN3TEfyhjLRm3r3mqknqydgVTB74XG8l4jMLwEACEeihVg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -315,9 +313,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@biomejs/cli-win32-x64": {
|
"node_modules/@biomejs/cli-win32-x64": {
|
||||||
"version": "2.4.10",
|
"version": "2.4.11",
|
||||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.4.10.tgz",
|
"resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.4.11.tgz",
|
||||||
"integrity": "sha512-aW/JU5GuyH4uxMrNYpoC2kjaHlyJGLgIa3XkhPEZI0uKhZhJZU8BuEyJmvgzSPQNGozBwWjC972RaNdcJ9KyJg==",
|
"integrity": "sha512-A8D3JM/00C2KQgUV3oj8Ba15EHEYwebAGCy5Sf9GAjr5Y3+kJIYOiESoqRDeuRZueuMdCsbLZIUqmPhpYXJE9A==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -365,9 +363,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@csstools/css-calc": {
|
"node_modules/@csstools/css-calc": {
|
||||||
"version": "3.1.1",
|
"version": "3.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.2.0.tgz",
|
||||||
"integrity": "sha512-HJ26Z/vmsZQqs/o3a6bgKslXGFAungXGbinULZO3eMsOyNJHeBBZfup5FiZInOghgoM4Hwnmw+OgbJCNg1wwUQ==",
|
"integrity": "sha512-bR9e6o2BDB12jzN/gIbjHa5wLJ4UjD1CB9pM7ehlc0ddk6EBz+yYS1EV2MF55/HUxrHcB/hehAyt5vhsA3hx7w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -389,9 +387,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@csstools/css-color-parser": {
|
"node_modules/@csstools/css-color-parser": {
|
||||||
"version": "4.0.2",
|
"version": "4.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.1.0.tgz",
|
||||||
"integrity": "sha512-0GEfbBLmTFf0dJlpsNU7zwxRIH0/BGEMuXLTCvFYxuL1tNhqzTbtnFICyJLTNK4a+RechKP75e7w42ClXSnJQw==",
|
"integrity": "sha512-U0KhLYmy2GVj6q4T3WaAe6NPuFYCPQoE3b0dRGxejWDgcPp8TP7S5rVdM5ZrFaqu4N67X8YaPBw14dQSYx3IyQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -406,7 +404,7 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@csstools/color-helpers": "^6.0.2",
|
"@csstools/color-helpers": "^6.0.2",
|
||||||
"@csstools/css-calc": "^3.1.1"
|
"@csstools/css-calc": "^3.2.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=20.19.0"
|
"node": ">=20.19.0"
|
||||||
@@ -485,38 +483,35 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@emnapi/core": {
|
"node_modules/@emnapi/core": {
|
||||||
"version": "1.9.1",
|
"version": "1.9.2",
|
||||||
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.1.tgz",
|
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.2.tgz",
|
||||||
"integrity": "sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA==",
|
"integrity": "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@emnapi/wasi-threads": "1.2.0",
|
"@emnapi/wasi-threads": "1.2.1",
|
||||||
"tslib": "^2.4.0"
|
"tslib": "^2.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@emnapi/runtime": {
|
"node_modules/@emnapi/runtime": {
|
||||||
"version": "1.9.1",
|
"version": "1.9.2",
|
||||||
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.1.tgz",
|
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.2.tgz",
|
||||||
"integrity": "sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA==",
|
"integrity": "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tslib": "^2.4.0"
|
"tslib": "^2.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@emnapi/wasi-threads": {
|
"node_modules/@emnapi/wasi-threads": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz",
|
||||||
"integrity": "sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==",
|
"integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tslib": "^2.4.0"
|
"tslib": "^2.4.0"
|
||||||
}
|
}
|
||||||
@@ -568,9 +563,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@napi-rs/wasm-runtime": {
|
"node_modules/@napi-rs/wasm-runtime": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.3.tgz",
|
||||||
"integrity": "sha512-sNXv5oLJ7ob93xkZ1XnxisYhGYXfaG9f65/ZgYuAu3qt7b3NadcOEhLvx28hv31PgX8SZJRYrAIPQilQmFpLVw==",
|
"integrity": "sha512-xK9sGVbJWYb08+mTJt3/YV24WxvxpXcXtP6B172paPZ+Ts69Re9dAr7lKwJoeIx8OoeuimEiRZ7umkiUVClmmQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
@@ -587,9 +582,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@oxc-project/types": {
|
"node_modules/@oxc-project/types": {
|
||||||
"version": "0.122.0",
|
"version": "0.124.0",
|
||||||
"resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.122.0.tgz",
|
"resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.124.0.tgz",
|
||||||
"integrity": "sha512-oLAl5kBpV4w69UtFZ9xqcmTi+GENWOcPF7FCrczTiBbmC0ibXxCwyvZGbO39rCVEuLGAZM84DH0pUIyyv/YJzA==",
|
"integrity": "sha512-VBFWMTBvHxS11Z5Lvlr3IWgrwhMTXV+Md+EQF0Xf60+wAdsGFTBx7X7K/hP4pi8N7dcm1RvcHwDxZ16Qx8keUg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
@@ -613,9 +608,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-android-arm64": {
|
"node_modules/@rolldown/binding-android-arm64": {
|
||||||
"version": "1.0.0-rc.12",
|
"version": "1.0.0-rc.15",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.12.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.15.tgz",
|
||||||
"integrity": "sha512-pv1y2Fv0JybcykuiiD3qBOBdz6RteYojRFY1d+b95WVuzx211CRh+ytI/+9iVyWQ6koTh5dawe4S/yRfOFjgaA==",
|
"integrity": "sha512-YYe6aWruPZDtHNpwu7+qAHEMbQ/yRl6atqb/AhznLTnD3UY99Q1jE7ihLSahNWkF4EqRPVC4SiR4O0UkLK02tA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -630,9 +625,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-darwin-arm64": {
|
"node_modules/@rolldown/binding-darwin-arm64": {
|
||||||
"version": "1.0.0-rc.12",
|
"version": "1.0.0-rc.15",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.12.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.15.tgz",
|
||||||
"integrity": "sha512-cFYr6zTG/3PXXF3pUO+umXxt1wkRK/0AYT8lDwuqvRC+LuKYWSAQAQZjCWDQpAH172ZV6ieYrNnFzVVcnSflAg==",
|
"integrity": "sha512-oArR/ig8wNTPYsXL+Mzhs0oxhxfuHRfG7Ikw7jXsw8mYOtk71W0OkF2VEVh699pdmzjPQsTjlD1JIOoHkLP1Fg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -647,9 +642,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-darwin-x64": {
|
"node_modules/@rolldown/binding-darwin-x64": {
|
||||||
"version": "1.0.0-rc.12",
|
"version": "1.0.0-rc.15",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.12.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.15.tgz",
|
||||||
"integrity": "sha512-ZCsYknnHzeXYps0lGBz8JrF37GpE9bFVefrlmDrAQhOEi4IOIlcoU1+FwHEtyXGx2VkYAvhu7dyBf75EJQffBw==",
|
"integrity": "sha512-YzeVqOqjPYvUbJSWJ4EDL8ahbmsIXQpgL3JVipmN+MX0XnXMeWomLN3Fb+nwCmP/jfyqte5I3XRSm7OfQrbyxw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -664,9 +659,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-freebsd-x64": {
|
"node_modules/@rolldown/binding-freebsd-x64": {
|
||||||
"version": "1.0.0-rc.12",
|
"version": "1.0.0-rc.15",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.12.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.15.tgz",
|
||||||
"integrity": "sha512-dMLeprcVsyJsKolRXyoTH3NL6qtsT0Y2xeuEA8WQJquWFXkEC4bcu1rLZZSnZRMtAqwtrF/Ib9Ddtpa/Gkge9Q==",
|
"integrity": "sha512-9Erhx956jeQ0nNTyif1+QWAXDRD38ZNjr//bSHrt6wDwB+QkAfl2q6Mn1k6OBPerznjRmbM10lgRb1Pli4xZPw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -681,9 +676,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-linux-arm-gnueabihf": {
|
"node_modules/@rolldown/binding-linux-arm-gnueabihf": {
|
||||||
"version": "1.0.0-rc.12",
|
"version": "1.0.0-rc.15",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.12.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.15.tgz",
|
||||||
"integrity": "sha512-YqWjAgGC/9M1lz3GR1r1rP79nMgo3mQiiA+Hfo+pvKFK1fAJ1bCi0ZQVh8noOqNacuY1qIcfyVfP6HoyBRZ85Q==",
|
"integrity": "sha512-cVwk0w8QbZJGTnP/AHQBs5yNwmpgGYStL88t4UIaqcvYJWBfS0s3oqVLZPwsPU6M0zlW4GqjP0Zq5MnAGwFeGA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
@@ -698,9 +693,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-linux-arm64-gnu": {
|
"node_modules/@rolldown/binding-linux-arm64-gnu": {
|
||||||
"version": "1.0.0-rc.12",
|
"version": "1.0.0-rc.15",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.12.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.15.tgz",
|
||||||
"integrity": "sha512-/I5AS4cIroLpslsmzXfwbe5OmWvSsrFuEw3mwvbQ1kDxJ822hFHIx+vsN/TAzNVyepI/j/GSzrtCIwQPeKCLIg==",
|
"integrity": "sha512-eBZ/u8iAK9SoHGanqe/jrPnY0JvBN6iXbVOsbO38mbz+ZJsaobExAm1Iu+rxa4S1l2FjG0qEZn4Rc6X8n+9M+w==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -715,9 +710,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-linux-arm64-musl": {
|
"node_modules/@rolldown/binding-linux-arm64-musl": {
|
||||||
"version": "1.0.0-rc.12",
|
"version": "1.0.0-rc.15",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.12.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.15.tgz",
|
||||||
"integrity": "sha512-V6/wZztnBqlx5hJQqNWwFdxIKN0m38p8Jas+VoSfgH54HSj9tKTt1dZvG6JRHcjh6D7TvrJPWFGaY9UBVOaWPw==",
|
"integrity": "sha512-ZvRYMGrAklV9PEkgt4LQM6MjQX2P58HPAuecwYObY2DhS2t35R0I810bKi0wmaYORt6m/2Sm+Z+nFgb0WhXNcQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -732,9 +727,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-linux-ppc64-gnu": {
|
"node_modules/@rolldown/binding-linux-ppc64-gnu": {
|
||||||
"version": "1.0.0-rc.12",
|
"version": "1.0.0-rc.15",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.12.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.15.tgz",
|
||||||
"integrity": "sha512-AP3E9BpcUYliZCxa3w5Kwj9OtEVDYK6sVoUzy4vTOJsjPOgdaJZKFmN4oOlX0Wp0RPV2ETfmIra9x1xuayFB7g==",
|
"integrity": "sha512-VDpgGBzgfg5hLg+uBpCLoFG5kVvEyafmfxGUV0UHLcL5irxAK7PKNeC2MwClgk6ZAiNhmo9FLhRYgvMmedLtnQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ppc64"
|
"ppc64"
|
||||||
],
|
],
|
||||||
@@ -749,9 +744,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-linux-s390x-gnu": {
|
"node_modules/@rolldown/binding-linux-s390x-gnu": {
|
||||||
"version": "1.0.0-rc.12",
|
"version": "1.0.0-rc.15",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.12.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.15.tgz",
|
||||||
"integrity": "sha512-nWwpvUSPkoFmZo0kQazZYOrT7J5DGOJ/+QHHzjvNlooDZED8oH82Yg67HvehPPLAg5fUff7TfWFHQS8IV1n3og==",
|
"integrity": "sha512-y1uXY3qQWCzcPgRJATPSOUP4tCemh4uBdY7e3EZbVwCJTY3gLJWnQABgeUetvED+bt1FQ01OeZwvhLS2bpNrAQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"s390x"
|
"s390x"
|
||||||
],
|
],
|
||||||
@@ -766,9 +761,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-linux-x64-gnu": {
|
"node_modules/@rolldown/binding-linux-x64-gnu": {
|
||||||
"version": "1.0.0-rc.12",
|
"version": "1.0.0-rc.15",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.12.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.15.tgz",
|
||||||
"integrity": "sha512-RNrafz5bcwRy+O9e6P8Z/OCAJW/A+qtBczIqVYwTs14pf4iV1/+eKEjdOUta93q2TsT/FI0XYDP3TCky38LMAg==",
|
"integrity": "sha512-023bTPBod7J3Y/4fzAN6QtpkSABR0rigtrwaP+qSEabUh5zf6ELr9Nc7GujaROuPY3uwdSIXWrvhn1KxOvurWA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -783,9 +778,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-linux-x64-musl": {
|
"node_modules/@rolldown/binding-linux-x64-musl": {
|
||||||
"version": "1.0.0-rc.12",
|
"version": "1.0.0-rc.15",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.12.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.15.tgz",
|
||||||
"integrity": "sha512-Jpw/0iwoKWx3LJ2rc1yjFrj+T7iHZn2JDg1Yny1ma0luviFS4mhAIcd1LFNxK3EYu3DHWCps0ydXQ5i/rrJ2ig==",
|
"integrity": "sha512-witB2O0/hU4CgfOOKUoeFgQ4GktPi1eEbAhaLAIpgD6+ZnhcPkUtPsoKKHRzmOoWPZue46IThdSgdo4XneOLYw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -800,9 +795,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-openharmony-arm64": {
|
"node_modules/@rolldown/binding-openharmony-arm64": {
|
||||||
"version": "1.0.0-rc.12",
|
"version": "1.0.0-rc.15",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.12.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.15.tgz",
|
||||||
"integrity": "sha512-vRugONE4yMfVn0+7lUKdKvN4D5YusEiPilaoO2sgUWpCvrncvWgPMzK00ZFFJuiPgLwgFNP5eSiUlv2tfc+lpA==",
|
"integrity": "sha512-UCL68NJ0Ud5zRipXZE9dF5PmirzJE4E4BCIOOssEnM7wLDsxjc6Qb0sGDxTNRTP53I6MZpygyCpY8Aa8sPfKPg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -817,9 +812,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-wasm32-wasi": {
|
"node_modules/@rolldown/binding-wasm32-wasi": {
|
||||||
"version": "1.0.0-rc.12",
|
"version": "1.0.0-rc.15",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.12.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.15.tgz",
|
||||||
"integrity": "sha512-ykGiLr/6kkiHc0XnBfmFJuCjr5ZYKKofkx+chJWDjitX+KsJuAmrzWhwyOMSHzPhzOHOy7u9HlFoa5MoAOJ/Zg==",
|
"integrity": "sha512-ApLruZq/ig+nhaE7OJm4lDjayUnOHVUa77zGeqnqZ9pn0ovdVbbNPerVibLXDmWeUZXjIYIT8V3xkT58Rm9u5Q==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"wasm32"
|
"wasm32"
|
||||||
],
|
],
|
||||||
@@ -827,16 +822,18 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@napi-rs/wasm-runtime": "^1.1.1"
|
"@emnapi/core": "1.9.2",
|
||||||
|
"@emnapi/runtime": "1.9.2",
|
||||||
|
"@napi-rs/wasm-runtime": "^1.1.3"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=14.0.0"
|
"node": ">=14.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-win32-arm64-msvc": {
|
"node_modules/@rolldown/binding-win32-arm64-msvc": {
|
||||||
"version": "1.0.0-rc.12",
|
"version": "1.0.0-rc.15",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.12.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.15.tgz",
|
||||||
"integrity": "sha512-5eOND4duWkwx1AzCxadcOrNeighiLwMInEADT0YM7xeEOOFcovWZCq8dadXgcRHSf3Ulh1kFo/qvzoFiCLOL1Q==",
|
"integrity": "sha512-KmoUoU7HnN+Si5YWJigfTws1jz1bKBYDQKdbLspz0UaqjjFkddHsqorgiW1mxcAj88lYUE6NC/zJNwT+SloqtA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -851,9 +848,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-win32-x64-msvc": {
|
"node_modules/@rolldown/binding-win32-x64-msvc": {
|
||||||
"version": "1.0.0-rc.12",
|
"version": "1.0.0-rc.15",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.12.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.15.tgz",
|
||||||
"integrity": "sha512-PyqoipaswDLAZtot351MLhrlrh6lcZPo2LSYE+VDxbVk24LVKAGOuE4hb8xZQmrPAuEtTZW8E6D2zc5EUZX4Lw==",
|
"integrity": "sha512-3P2A8L+x75qavWLe/Dll3EYBJLQmtkJN8rfh+U/eR3MqMgL/h98PhYI+JFfXuDPgPeCB7iZAKiqii5vqOvnA0g==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -1023,13 +1020,13 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "25.5.2",
|
"version": "25.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.6.0.tgz",
|
||||||
"integrity": "sha512-tO4ZIRKNC+MDWV4qKVZe3Ql/woTnmHDr5JD8UI5hn2pwBrHEwOEMZK7WlNb5RKB6EoJ02gwmQS9OrjuFnZYdpg==",
|
"integrity": "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"undici-types": "~7.18.0"
|
"undici-types": "~7.19.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@types/react": {
|
"node_modules/@types/react": {
|
||||||
@@ -1102,14 +1099,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vitest/coverage-v8": {
|
"node_modules/@vitest/coverage-v8": {
|
||||||
"version": "4.1.2",
|
"version": "4.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.4.tgz",
|
||||||
"integrity": "sha512-sPK//PHO+kAkScb8XITeB1bf7fsk85Km7+rt4eeuRR3VS1/crD47cmV5wicisJmjNdfeokTZwjMk4Mj2d58Mgg==",
|
"integrity": "sha512-x7FptB5oDruxNPDNY2+S8tCh0pcq7ymCe1gTHcsp733jYjrJl8V1gMUlVysuCD9Kz46Xz9t1akkv08dPcYDs1w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@bcoe/v8-coverage": "^1.0.2",
|
"@bcoe/v8-coverage": "^1.0.2",
|
||||||
"@vitest/utils": "4.1.2",
|
"@vitest/utils": "4.1.4",
|
||||||
"ast-v8-to-istanbul": "^1.0.0",
|
"ast-v8-to-istanbul": "^1.0.0",
|
||||||
"istanbul-lib-coverage": "^3.2.2",
|
"istanbul-lib-coverage": "^3.2.2",
|
||||||
"istanbul-lib-report": "^3.0.1",
|
"istanbul-lib-report": "^3.0.1",
|
||||||
@@ -1123,8 +1120,8 @@
|
|||||||
"url": "https://opencollective.com/vitest"
|
"url": "https://opencollective.com/vitest"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@vitest/browser": "4.1.2",
|
"@vitest/browser": "4.1.4",
|
||||||
"vitest": "4.1.2"
|
"vitest": "4.1.4"
|
||||||
},
|
},
|
||||||
"peerDependenciesMeta": {
|
"peerDependenciesMeta": {
|
||||||
"@vitest/browser": {
|
"@vitest/browser": {
|
||||||
@@ -1133,16 +1130,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vitest/expect": {
|
"node_modules/@vitest/expect": {
|
||||||
"version": "4.1.2",
|
"version": "4.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.4.tgz",
|
||||||
"integrity": "sha512-gbu+7B0YgUJ2nkdsRJrFFW6X7NTP44WlhiclHniUhxADQJH5Szt9mZ9hWnJPJ8YwOK5zUOSSlSvyzRf0u1DSBQ==",
|
"integrity": "sha512-iPBpra+VDuXmBFI3FMKHSFXp3Gx5HfmSCE8X67Dn+bwephCnQCaB7qWK2ldHa+8ncN8hJU8VTMcxjPpyMkUjww==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@standard-schema/spec": "^1.1.0",
|
"@standard-schema/spec": "^1.1.0",
|
||||||
"@types/chai": "^5.2.2",
|
"@types/chai": "^5.2.2",
|
||||||
"@vitest/spy": "4.1.2",
|
"@vitest/spy": "4.1.4",
|
||||||
"@vitest/utils": "4.1.2",
|
"@vitest/utils": "4.1.4",
|
||||||
"chai": "^6.2.2",
|
"chai": "^6.2.2",
|
||||||
"tinyrainbow": "^3.1.0"
|
"tinyrainbow": "^3.1.0"
|
||||||
},
|
},
|
||||||
@@ -1151,13 +1148,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vitest/mocker": {
|
"node_modules/@vitest/mocker": {
|
||||||
"version": "4.1.2",
|
"version": "4.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.4.tgz",
|
||||||
"integrity": "sha512-Ize4iQtEALHDttPRCmN+FKqOl2vxTiNUhzobQFFt/BM1lRUTG7zRCLOykG/6Vo4E4hnUdfVLo5/eqKPukcWW7Q==",
|
"integrity": "sha512-R9HTZBhW6yCSGbGQnDnH3QHfJxokKN4KB+Yvk9Q1le7eQNYwiCyKxmLmurSpFy6BzJanSLuEUDrD+j97Q+ZLPg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vitest/spy": "4.1.2",
|
"@vitest/spy": "4.1.4",
|
||||||
"estree-walker": "^3.0.3",
|
"estree-walker": "^3.0.3",
|
||||||
"magic-string": "^0.30.21"
|
"magic-string": "^0.30.21"
|
||||||
},
|
},
|
||||||
@@ -1178,9 +1175,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vitest/pretty-format": {
|
"node_modules/@vitest/pretty-format": {
|
||||||
"version": "4.1.2",
|
"version": "4.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.4.tgz",
|
||||||
"integrity": "sha512-dwQga8aejqeuB+TvXCMzSQemvV9hNEtDDpgUKDzOmNQayl2OG241PSWeJwKRH3CiC+sESrmoFd49rfnq7T4RnA==",
|
"integrity": "sha512-ddmDHU0gjEUyEVLxtZa7xamrpIefdEETu3nZjWtHeZX4QxqJ7tRxSteHVXJOcr8jhiLoGAhkK4WJ3WqBpjx42A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -1191,13 +1188,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vitest/runner": {
|
"node_modules/@vitest/runner": {
|
||||||
"version": "4.1.2",
|
"version": "4.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.4.tgz",
|
||||||
"integrity": "sha512-Gr+FQan34CdiYAwpGJmQG8PgkyFVmARK8/xSijia3eTFgVfpcpztWLuP6FttGNfPLJhaZVP/euvujeNYar36OQ==",
|
"integrity": "sha512-xTp7VZ5aXP5ZJrn15UtJUWlx6qXLnGtF6jNxHepdPHpMfz/aVPx+htHtgcAL2mDXJgKhpoo2e9/hVJsIeFbytQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vitest/utils": "4.1.2",
|
"@vitest/utils": "4.1.4",
|
||||||
"pathe": "^2.0.3"
|
"pathe": "^2.0.3"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
@@ -1205,14 +1202,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vitest/snapshot": {
|
"node_modules/@vitest/snapshot": {
|
||||||
"version": "4.1.2",
|
"version": "4.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.4.tgz",
|
||||||
"integrity": "sha512-g7yfUmxYS4mNxk31qbOYsSt2F4m1E02LFqO53Xpzg3zKMhLAPZAjjfyl9e6z7HrW6LvUdTwAQR3HHfLjpko16A==",
|
"integrity": "sha512-MCjCFgaS8aZz+m5nTcEcgk/xhWv0rEH4Yl53PPlMXOZ1/Ka2VcZU6CJ+MgYCZbcJvzGhQRjVrGQNZqkGPttIKw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vitest/pretty-format": "4.1.2",
|
"@vitest/pretty-format": "4.1.4",
|
||||||
"@vitest/utils": "4.1.2",
|
"@vitest/utils": "4.1.4",
|
||||||
"magic-string": "^0.30.21",
|
"magic-string": "^0.30.21",
|
||||||
"pathe": "^2.0.3"
|
"pathe": "^2.0.3"
|
||||||
},
|
},
|
||||||
@@ -1221,9 +1218,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vitest/spy": {
|
"node_modules/@vitest/spy": {
|
||||||
"version": "4.1.2",
|
"version": "4.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.4.tgz",
|
||||||
"integrity": "sha512-DU4fBnbVCJGNBwVA6xSToNXrkZNSiw59H8tcuUspVMsBDBST4nfvsPsEHDHGtWRRnqBERBQu7TrTKskmjqTXKA==",
|
"integrity": "sha512-XxNdAsKW7C+FLydqFJLb5KhJtl3PGCMmYwFRfhvIgxJvLSXhhVI1zM8f1qD3Zg7RCjTSzDVyct6sghs9UEgBEQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
@@ -1231,13 +1228,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vitest/utils": {
|
"node_modules/@vitest/utils": {
|
||||||
"version": "4.1.2",
|
"version": "4.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.4.tgz",
|
||||||
"integrity": "sha512-xw2/TiX82lQHA06cgbqRKFb5lCAy3axQ4H4SoUFhUsg+wztiet+co86IAMDtF6Vm1hc7J6j09oh/rgDn+JdKIQ==",
|
"integrity": "sha512-13QMT+eysM5uVGa1rG4kegGYNp6cnQcsTc67ELFbhNLQO+vgsygtYJx2khvdt4gVQqSSpC/KT5FZZxUpP3Oatw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vitest/pretty-format": "4.1.2",
|
"@vitest/pretty-format": "4.1.4",
|
||||||
"convert-source-map": "^2.0.0",
|
"convert-source-map": "^2.0.0",
|
||||||
"tinyrainbow": "^3.1.0"
|
"tinyrainbow": "^3.1.0"
|
||||||
},
|
},
|
||||||
@@ -1539,9 +1536,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/i18next": {
|
"node_modules/i18next": {
|
||||||
"version": "26.0.3",
|
"version": "26.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/i18next/-/i18next-26.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/i18next/-/i18next-26.0.4.tgz",
|
||||||
"integrity": "sha512-1571kXINxHKY7LksWp8wP+zP0YqHSSpl/OW0Y0owFEf2H3s8gCAffWaZivcz14rMkOvn3R/psiQxVsR9t2Nafg==",
|
"integrity": "sha512-gXF7U9bfioXPLv7mw8Qt2nfO7vij5MyINvPgVv99pX3fL1Y01pw2mKBFrlYpRxRCl2wz3ISenj6VsMJT2isfuA==",
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "individual",
|
"type": "individual",
|
||||||
@@ -1643,14 +1640,14 @@
|
|||||||
"peer": true
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/jsdom": {
|
"node_modules/jsdom": {
|
||||||
"version": "29.0.1",
|
"version": "29.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-29.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-29.0.2.tgz",
|
||||||
"integrity": "sha512-z6JOK5gRO7aMybVq/y/MlIpKh8JIi68FBKMUtKkK2KH/wMSRlCxQ682d08LB9fYXplyY/UXG8P4XXTScmdjApg==",
|
"integrity": "sha512-9VnGEBosc/ZpwyOsJBCQ/3I5p7Q5ngOY14a9bf5btenAORmZfDse1ZEheMiWcJ3h81+Fv7HmJFdS0szo/waF2w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@asamuzakjp/css-color": "^5.0.1",
|
"@asamuzakjp/css-color": "^5.1.5",
|
||||||
"@asamuzakjp/dom-selector": "^7.0.3",
|
"@asamuzakjp/dom-selector": "^7.0.6",
|
||||||
"@bramus/specificity": "^2.4.2",
|
"@bramus/specificity": "^2.4.2",
|
||||||
"@csstools/css-syntax-patches-for-csstree": "^1.1.1",
|
"@csstools/css-syntax-patches-for-csstree": "^1.1.1",
|
||||||
"@exodus/bytes": "^1.15.0",
|
"@exodus/bytes": "^1.15.0",
|
||||||
@@ -1945,9 +1942,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/lru-cache": {
|
"node_modules/lru-cache": {
|
||||||
"version": "11.2.7",
|
"version": "11.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.7.tgz",
|
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.3.3.tgz",
|
||||||
"integrity": "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==",
|
"integrity": "sha512-JvNw9Y81y33E+BEYPr0U7omo+U9AySnsMsEiXgwT6yqd31VQWTLNQqmT4ou5eqPFUrTfIDFta2wKhB1hyohtAQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "BlueOak-1.0.0",
|
"license": "BlueOak-1.0.0",
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -1955,9 +1952,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/lucide-react": {
|
"node_modules/lucide-react": {
|
||||||
"version": "1.7.0",
|
"version": "1.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-1.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-1.8.0.tgz",
|
||||||
"integrity": "sha512-yI7BeItCLZJTXikmK4KNUGCKoGzSvbKlfCvw44bU4fXAL6v3gYS4uHD1jzsLkfwODYwI6Drw5Tu9Z5ulDe0TSg==",
|
"integrity": "sha512-WuvlsjngSk7TnTBJ1hsCy3ql9V9VOdcPkd3PKcSmM34vJD8KG6molxz7m7zbYFgICwsanQWmJ13JlYs4Zp7Arw==",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
"react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
@@ -2187,24 +2184,24 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react": {
|
"node_modules/react": {
|
||||||
"version": "19.2.4",
|
"version": "19.2.5",
|
||||||
"resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/react/-/react-19.2.5.tgz",
|
||||||
"integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==",
|
"integrity": "sha512-llUJLzz1zTUBrskt2pwZgLq59AemifIftw4aB7JxOqf1HY2FDaGDxgwpAPVzHU1kdWabH7FauP4i1oEeer2WCA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-dom": {
|
"node_modules/react-dom": {
|
||||||
"version": "19.2.4",
|
"version": "19.2.5",
|
||||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.5.tgz",
|
||||||
"integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==",
|
"integrity": "sha512-J5bAZz+DXMMwW/wV3xzKke59Af6CHY7G4uYLN1OvBcKEsWOs4pQExj86BBKamxl/Ik5bx9whOrvBlSDfWzgSag==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"scheduler": "^0.27.0"
|
"scheduler": "^0.27.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": "^19.2.4"
|
"react": "^19.2.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-i18next": {
|
"node_modules/react-i18next": {
|
||||||
@@ -2305,14 +2302,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/rolldown": {
|
"node_modules/rolldown": {
|
||||||
"version": "1.0.0-rc.12",
|
"version": "1.0.0-rc.15",
|
||||||
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.12.tgz",
|
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.15.tgz",
|
||||||
"integrity": "sha512-yP4USLIMYrwpPHEFB5JGH1uxhcslv6/hL0OyvTuY+3qlOSJvZ7ntYnoWpehBxufkgN0cvXxppuTu5hHa/zPh+A==",
|
"integrity": "sha512-Ff31guA5zT6WjnGp0SXw76X6hzGRk/OQq2hE+1lcDe+lJdHSgnSX6nK3erbONHyCbpSj9a9E+uX/OvytZoWp2g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@oxc-project/types": "=0.122.0",
|
"@oxc-project/types": "=0.124.0",
|
||||||
"@rolldown/pluginutils": "1.0.0-rc.12"
|
"@rolldown/pluginutils": "1.0.0-rc.15"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"rolldown": "bin/cli.mjs"
|
"rolldown": "bin/cli.mjs"
|
||||||
@@ -2321,27 +2318,27 @@
|
|||||||
"node": "^20.19.0 || >=22.12.0"
|
"node": "^20.19.0 || >=22.12.0"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@rolldown/binding-android-arm64": "1.0.0-rc.12",
|
"@rolldown/binding-android-arm64": "1.0.0-rc.15",
|
||||||
"@rolldown/binding-darwin-arm64": "1.0.0-rc.12",
|
"@rolldown/binding-darwin-arm64": "1.0.0-rc.15",
|
||||||
"@rolldown/binding-darwin-x64": "1.0.0-rc.12",
|
"@rolldown/binding-darwin-x64": "1.0.0-rc.15",
|
||||||
"@rolldown/binding-freebsd-x64": "1.0.0-rc.12",
|
"@rolldown/binding-freebsd-x64": "1.0.0-rc.15",
|
||||||
"@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.12",
|
"@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.15",
|
||||||
"@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.12",
|
"@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.15",
|
||||||
"@rolldown/binding-linux-arm64-musl": "1.0.0-rc.12",
|
"@rolldown/binding-linux-arm64-musl": "1.0.0-rc.15",
|
||||||
"@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.12",
|
"@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.15",
|
||||||
"@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.12",
|
"@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.15",
|
||||||
"@rolldown/binding-linux-x64-gnu": "1.0.0-rc.12",
|
"@rolldown/binding-linux-x64-gnu": "1.0.0-rc.15",
|
||||||
"@rolldown/binding-linux-x64-musl": "1.0.0-rc.12",
|
"@rolldown/binding-linux-x64-musl": "1.0.0-rc.15",
|
||||||
"@rolldown/binding-openharmony-arm64": "1.0.0-rc.12",
|
"@rolldown/binding-openharmony-arm64": "1.0.0-rc.15",
|
||||||
"@rolldown/binding-wasm32-wasi": "1.0.0-rc.12",
|
"@rolldown/binding-wasm32-wasi": "1.0.0-rc.15",
|
||||||
"@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.12",
|
"@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.15",
|
||||||
"@rolldown/binding-win32-x64-msvc": "1.0.0-rc.12"
|
"@rolldown/binding-win32-x64-msvc": "1.0.0-rc.15"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/rolldown/node_modules/@rolldown/pluginutils": {
|
"node_modules/rolldown/node_modules/@rolldown/pluginutils": {
|
||||||
"version": "1.0.0-rc.12",
|
"version": "1.0.0-rc.15",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.12.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.15.tgz",
|
||||||
"integrity": "sha512-HHMwmarRKvoFsJorqYlFeFRzXZqCt2ETQlEDOb9aqssrnVBB1/+xgTGtuTrIk5vzLNX1MjMtTf7W9z3tsSbrxw==",
|
"integrity": "sha512-UromN0peaE53IaBRe9W7CjrZgXl90fqGpK+mIZbA3qSTeYqg3pqpROBdIPvOG3F5ereDHNwoHBI2e50n1BDr1g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
@@ -2570,9 +2567,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/undici-types": {
|
"node_modules/undici-types": {
|
||||||
"version": "7.18.2",
|
"version": "7.19.2",
|
||||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz",
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.19.2.tgz",
|
||||||
"integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==",
|
"integrity": "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
@@ -2586,16 +2583,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/vite": {
|
"node_modules/vite": {
|
||||||
"version": "8.0.5",
|
"version": "8.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-8.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-8.0.8.tgz",
|
||||||
"integrity": "sha512-nmu43Qvq9UopTRfMx2jOYW5l16pb3iDC1JH6yMuPkpVbzK0k+L7dfsEDH4jRgYFmsg0sTAqkojoZgzLMlwHsCQ==",
|
"integrity": "sha512-dbU7/iLVa8KZALJyLOBOQ88nOXtNG8vxKuOT4I2mD+Ya70KPceF4IAmDsmU0h1Qsn5bPrvsY9HJstCRh3hG6Uw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"lightningcss": "^1.32.0",
|
"lightningcss": "^1.32.0",
|
||||||
"picomatch": "^4.0.4",
|
"picomatch": "^4.0.4",
|
||||||
"postcss": "^8.5.8",
|
"postcss": "^8.5.8",
|
||||||
"rolldown": "1.0.0-rc.12",
|
"rolldown": "1.0.0-rc.15",
|
||||||
"tinyglobby": "^0.2.15"
|
"tinyglobby": "^0.2.15"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
@@ -2679,19 +2676,19 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/vitest": {
|
"node_modules/vitest": {
|
||||||
"version": "4.1.2",
|
"version": "4.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.4.tgz",
|
||||||
"integrity": "sha512-xjR1dMTVHlFLh98JE3i/f/WePqJsah4A0FK9cc8Ehp9Udk0AZk6ccpIZhh1qJ/yxVWRZ+Q54ocnD8TXmkhspGg==",
|
"integrity": "sha512-tFuJqTxKb8AvfyqMfnavXdzfy3h3sWZRWwfluGbkeR7n0HUev+FmNgZ8SDrRBTVrVCjgH5cA21qGbCffMNtWvg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vitest/expect": "4.1.2",
|
"@vitest/expect": "4.1.4",
|
||||||
"@vitest/mocker": "4.1.2",
|
"@vitest/mocker": "4.1.4",
|
||||||
"@vitest/pretty-format": "4.1.2",
|
"@vitest/pretty-format": "4.1.4",
|
||||||
"@vitest/runner": "4.1.2",
|
"@vitest/runner": "4.1.4",
|
||||||
"@vitest/snapshot": "4.1.2",
|
"@vitest/snapshot": "4.1.4",
|
||||||
"@vitest/spy": "4.1.2",
|
"@vitest/spy": "4.1.4",
|
||||||
"@vitest/utils": "4.1.2",
|
"@vitest/utils": "4.1.4",
|
||||||
"es-module-lexer": "^2.0.0",
|
"es-module-lexer": "^2.0.0",
|
||||||
"expect-type": "^1.3.0",
|
"expect-type": "^1.3.0",
|
||||||
"magic-string": "^0.30.21",
|
"magic-string": "^0.30.21",
|
||||||
@@ -2719,10 +2716,12 @@
|
|||||||
"@edge-runtime/vm": "*",
|
"@edge-runtime/vm": "*",
|
||||||
"@opentelemetry/api": "^1.9.0",
|
"@opentelemetry/api": "^1.9.0",
|
||||||
"@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0",
|
"@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0",
|
||||||
"@vitest/browser-playwright": "4.1.2",
|
"@vitest/browser-playwright": "4.1.4",
|
||||||
"@vitest/browser-preview": "4.1.2",
|
"@vitest/browser-preview": "4.1.4",
|
||||||
"@vitest/browser-webdriverio": "4.1.2",
|
"@vitest/browser-webdriverio": "4.1.4",
|
||||||
"@vitest/ui": "4.1.2",
|
"@vitest/coverage-istanbul": "4.1.4",
|
||||||
|
"@vitest/coverage-v8": "4.1.4",
|
||||||
|
"@vitest/ui": "4.1.4",
|
||||||
"happy-dom": "*",
|
"happy-dom": "*",
|
||||||
"jsdom": "*",
|
"jsdom": "*",
|
||||||
"vite": "^6.0.0 || ^7.0.0 || ^8.0.0"
|
"vite": "^6.0.0 || ^7.0.0 || ^8.0.0"
|
||||||
@@ -2746,6 +2745,12 @@
|
|||||||
"@vitest/browser-webdriverio": {
|
"@vitest/browser-webdriverio": {
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
|
"@vitest/coverage-istanbul": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@vitest/coverage-v8": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
"@vitest/ui": {
|
"@vitest/ui": {
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
|
|||||||
+10
-10
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "medassist-ng-frontend",
|
"name": "medassist-ng-frontend",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.22.2",
|
"version": "1.23.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
@@ -27,30 +27,30 @@
|
|||||||
"test:e2e:report": "playwright show-report"
|
"test:e2e:report": "playwright show-report"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"i18next": "^26.0.3",
|
"i18next": "^26.0.4",
|
||||||
"i18next-browser-languagedetector": "^8.2.1",
|
"i18next-browser-languagedetector": "^8.2.1",
|
||||||
"lucide-react": "^1.7.0",
|
"lucide-react": "^1.8.0",
|
||||||
"react": "^19.2.4",
|
"react": "^19.2.5",
|
||||||
"react-dom": "^19.2.4",
|
"react-dom": "^19.2.5",
|
||||||
"react-i18next": "^17.0.2",
|
"react-i18next": "^17.0.2",
|
||||||
"react-router-dom": "^7.14.0",
|
"react-router-dom": "^7.14.0",
|
||||||
"zod": "^4.3.6"
|
"zod": "^4.3.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@biomejs/biome": "^2.4.10",
|
"@biomejs/biome": "^2.4.11",
|
||||||
"@playwright/test": "^1.59.1",
|
"@playwright/test": "^1.59.1",
|
||||||
"@testing-library/jest-dom": "^6.9.1",
|
"@testing-library/jest-dom": "^6.9.1",
|
||||||
"@testing-library/react": "^16.3.2",
|
"@testing-library/react": "^16.3.2",
|
||||||
"@testing-library/user-event": "^14.6.1",
|
"@testing-library/user-event": "^14.6.1",
|
||||||
"@types/node": "^25.5.2",
|
"@types/node": "^25.6.0",
|
||||||
"@types/react": "^19.2.14",
|
"@types/react": "^19.2.14",
|
||||||
"@types/react-dom": "^19.2.3",
|
"@types/react-dom": "^19.2.3",
|
||||||
"@types/react-router-dom": "^5.3.3",
|
"@types/react-router-dom": "^5.3.3",
|
||||||
"@vitejs/plugin-react": "^6.0.1",
|
"@vitejs/plugin-react": "^6.0.1",
|
||||||
"@vitest/coverage-v8": "^4.1.2",
|
"@vitest/coverage-v8": "^4.1.4",
|
||||||
"jsdom": "^29.0.1",
|
"jsdom": "^29.0.2",
|
||||||
"typescript": "^6.0.2",
|
"typescript": "^6.0.2",
|
||||||
"vite": "^8.0.5",
|
"vite": "^8.0.8",
|
||||||
"vitest": "^4.1.0"
|
"vitest": "^4.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,9 @@ import { useTranslation } from "react-i18next";
|
|||||||
import { log } from "../utils/logger";
|
import { log } from "../utils/logger";
|
||||||
|
|
||||||
export interface Settings {
|
export interface Settings {
|
||||||
|
timezone: string;
|
||||||
|
availableTimezones: string[];
|
||||||
|
serverTimezone: string;
|
||||||
emailEnabled: boolean;
|
emailEnabled: boolean;
|
||||||
notificationEmail: string;
|
notificationEmail: string;
|
||||||
reminderDaysBefore: number;
|
reminderDaysBefore: number;
|
||||||
@@ -58,6 +61,9 @@ export interface Settings {
|
|||||||
export type SettingsLoadError = "auth" | "forbidden" | "request" | null;
|
export type SettingsLoadError = "auth" | "forbidden" | "request" | null;
|
||||||
|
|
||||||
const defaultSettings: Settings = {
|
const defaultSettings: Settings = {
|
||||||
|
timezone: "",
|
||||||
|
availableTimezones: [],
|
||||||
|
serverTimezone: "UTC",
|
||||||
emailEnabled: false,
|
emailEnabled: false,
|
||||||
notificationEmail: "",
|
notificationEmail: "",
|
||||||
reminderDaysBefore: 7,
|
reminderDaysBefore: 7,
|
||||||
@@ -243,6 +249,7 @@ export function useSettings(): UseSettingsReturn {
|
|||||||
const repeatDailyReminders = hasAnyStockReminder ? settingsToSave.repeatDailyReminders : false;
|
const repeatDailyReminders = hasAnyStockReminder ? settingsToSave.repeatDailyReminders : false;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
timezone: settingsToSave.timezone,
|
||||||
emailEnabled: effectiveEmailEnabled,
|
emailEnabled: effectiveEmailEnabled,
|
||||||
notificationEmail: settingsToSave.notificationEmail,
|
notificationEmail: settingsToSave.notificationEmail,
|
||||||
reminderDaysBefore: settingsToSave.reminderDaysBefore,
|
reminderDaysBefore: settingsToSave.reminderDaysBefore,
|
||||||
|
|||||||
@@ -389,6 +389,14 @@
|
|||||||
"title": "Sprache",
|
"title": "Sprache",
|
||||||
"select": "Sprache auswählen"
|
"select": "Sprache auswählen"
|
||||||
},
|
},
|
||||||
|
"timezone": {
|
||||||
|
"select": "Zeitzone",
|
||||||
|
"hint": "IANA-Zeitzone wählen. Wenn gesetzt, überschreibt sie die Server-TZ für deine Reminder-Zeitpunkte.",
|
||||||
|
"useServerDefault": "Server-Standard nutzen",
|
||||||
|
"currentServerTz": "Server-Standardzeitzone: {{timezone}}",
|
||||||
|
"saving": "Zeitzone wird gespeichert...",
|
||||||
|
"saved": "Zeitzone gespeichert"
|
||||||
|
},
|
||||||
"apiKey": {
|
"apiKey": {
|
||||||
"title": "API-Zugriff",
|
"title": "API-Zugriff",
|
||||||
"generateTitle": "API-Key erzeugen",
|
"generateTitle": "API-Key erzeugen",
|
||||||
|
|||||||
@@ -389,6 +389,14 @@
|
|||||||
"title": "Language",
|
"title": "Language",
|
||||||
"select": "Select language"
|
"select": "Select language"
|
||||||
},
|
},
|
||||||
|
"timezone": {
|
||||||
|
"select": "Timezone",
|
||||||
|
"hint": "Select an IANA timezone. When set, this overrides server TZ for your reminder timing.",
|
||||||
|
"useServerDefault": "Use server default",
|
||||||
|
"currentServerTz": "Server default timezone: {{timezone}}",
|
||||||
|
"saving": "Saving timezone...",
|
||||||
|
"saved": "Timezone saved"
|
||||||
|
},
|
||||||
"apiKey": {
|
"apiKey": {
|
||||||
"title": "API Access",
|
"title": "API Access",
|
||||||
"generateTitle": "Generate API key",
|
"generateTitle": "Generate API key",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/* biome-ignore-all lint/a11y/noLabelWithoutControl: settings rows use label-styled text with adjacent custom toggle controls */
|
/* biome-ignore-all lint/a11y/noLabelWithoutControl: settings rows use label-styled text with adjacent custom toggle controls */
|
||||||
import { useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { ConfirmModal, ExportModal } from "../components";
|
import { ConfirmModal, ExportModal } from "../components";
|
||||||
import { useAppContext } from "../context";
|
import { useAppContext } from "../context";
|
||||||
@@ -13,8 +13,11 @@ export function SettingsPage() {
|
|||||||
const [apiKeyError, setApiKeyError] = useState<string | null>(null);
|
const [apiKeyError, setApiKeyError] = useState<string | null>(null);
|
||||||
const {
|
const {
|
||||||
settings,
|
settings,
|
||||||
|
savedSettings,
|
||||||
setSettings,
|
setSettings,
|
||||||
settingsLoading,
|
settingsLoading,
|
||||||
|
settingsSaving,
|
||||||
|
settingsSaved,
|
||||||
settingsLoadError,
|
settingsLoadError,
|
||||||
// Email testing
|
// Email testing
|
||||||
testEmail,
|
testEmail,
|
||||||
@@ -39,6 +42,8 @@ export function SettingsPage() {
|
|||||||
setImportResult,
|
setImportResult,
|
||||||
meds,
|
meds,
|
||||||
} = useAppContext();
|
} = useAppContext();
|
||||||
|
const [timezoneTouched, setTimezoneTouched] = useState(false);
|
||||||
|
const [timezoneDraft, setTimezoneDraft] = useState("");
|
||||||
|
|
||||||
const hasExistingData = meds.length > 0;
|
const hasExistingData = meds.length > 0;
|
||||||
let emailUnavailableReason: string | null = null;
|
let emailUnavailableReason: string | null = null;
|
||||||
@@ -117,6 +122,49 @@ export function SettingsPage() {
|
|||||||
const automaticStockCalculationId = "settings-stock-calculation-automatic";
|
const automaticStockCalculationId = "settings-stock-calculation-automatic";
|
||||||
const manualStockCalculationId = "settings-stock-calculation-manual";
|
const manualStockCalculationId = "settings-stock-calculation-manual";
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setTimezoneDraft(settings.timezone);
|
||||||
|
}, [settings.timezone]);
|
||||||
|
|
||||||
|
const commitTimezoneDraft = () => {
|
||||||
|
if (timezoneDraft === settings.timezone) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimezoneTouched(true);
|
||||||
|
setSettings((prev) => ({ ...prev, timezone: timezoneDraft }));
|
||||||
|
};
|
||||||
|
|
||||||
|
const savedTimezone = savedSettings?.timezone ?? settings.timezone;
|
||||||
|
const timezoneChanged = settings.timezone !== savedTimezone;
|
||||||
|
const showTimezoneSaving = timezoneTouched && timezoneChanged && settingsSaving;
|
||||||
|
const showTimezoneSaved = timezoneTouched && !timezoneChanged && settingsSaved;
|
||||||
|
let timezoneStatusText = "";
|
||||||
|
if (showTimezoneSaving) {
|
||||||
|
timezoneStatusText = t("settings.timezone.saving");
|
||||||
|
} else if (showTimezoneSaved) {
|
||||||
|
timezoneStatusText = t("settings.timezone.saved");
|
||||||
|
}
|
||||||
|
const timezoneStatusClassName = showTimezoneSaved ? "timezone-status timezone-status-saved" : "timezone-status";
|
||||||
|
const availableTimezones = Array.isArray(settings.availableTimezones) ? settings.availableTimezones : [];
|
||||||
|
const timezoneSuggestions =
|
||||||
|
availableTimezones.length > 0
|
||||||
|
? availableTimezones
|
||||||
|
: (() => {
|
||||||
|
try {
|
||||||
|
type IntlWithSupportedValuesOf = typeof Intl & {
|
||||||
|
supportedValuesOf?: (key: string) => string[];
|
||||||
|
};
|
||||||
|
const intlWithSupportedValues = Intl as IntlWithSupportedValuesOf;
|
||||||
|
if (typeof intlWithSupportedValues.supportedValuesOf === "function") {
|
||||||
|
return intlWithSupportedValues.supportedValuesOf("timeZone");
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// fall through
|
||||||
|
}
|
||||||
|
return [settings.serverTimezone || "UTC", "UTC"];
|
||||||
|
})();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="grid">
|
<section className="grid">
|
||||||
{settingsLoading ? (
|
{settingsLoading ? (
|
||||||
@@ -160,6 +208,53 @@ export function SettingsPage() {
|
|||||||
<option value="de">🇩🇪 Deutsch</option>
|
<option value="de">🇩🇪 Deutsch</option>
|
||||||
</select>
|
</select>
|
||||||
</label>
|
</label>
|
||||||
|
<div className="setting-row language-row" style={{ marginTop: "12px" }}>
|
||||||
|
<div className="setting-label">
|
||||||
|
<span>{t("settings.timezone.select")}</span>
|
||||||
|
<span className="info-tooltip small tooltip-align-left" data-tooltip={t("settings.timezone.hint")}>
|
||||||
|
ⓘ
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="setting-actions" style={{ margin: 0, flexWrap: "nowrap", gap: "8px", width: "auto" }}>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
className="select-field language-select"
|
||||||
|
value={timezoneDraft}
|
||||||
|
onChange={(e) => {
|
||||||
|
setTimezoneDraft(e.target.value);
|
||||||
|
}}
|
||||||
|
onBlur={commitTimezoneDraft}
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (e.key === "Enter") {
|
||||||
|
e.preventDefault();
|
||||||
|
(e.currentTarget as HTMLInputElement).blur();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
list="settings-timezone-suggestions"
|
||||||
|
placeholder={settings.serverTimezone || "UTC"}
|
||||||
|
/>
|
||||||
|
<datalist id="settings-timezone-suggestions">
|
||||||
|
{timezoneSuggestions.map((zone) => (
|
||||||
|
<option key={zone} value={zone} />
|
||||||
|
))}
|
||||||
|
</datalist>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="ghost"
|
||||||
|
onClick={() => {
|
||||||
|
setTimezoneTouched(true);
|
||||||
|
setTimezoneDraft("");
|
||||||
|
setSettings((prev) => ({ ...prev, timezone: "" }));
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t("settings.timezone.useServerDefault")}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p className={timezoneStatusClassName}>{timezoneStatusText || " "}</p>
|
||||||
|
<p className="hint-text" style={{ marginTop: "8px" }}>
|
||||||
|
{t("settings.timezone.currentServerTz", { timezone: settings.serverTimezone || "UTC" })}
|
||||||
|
</p>
|
||||||
</article>
|
</article>
|
||||||
|
|
||||||
<article className="card" data-testid="settings-notification-card">
|
<article className="card" data-testid="settings-notification-card">
|
||||||
|
|||||||
Vendored
+1
-1
@@ -613,7 +613,7 @@ body.modal-open {
|
|||||||
grid-template-columns: repeat(auto-fit, minmax(min(320px, 100%), 1fr));
|
grid-template-columns: repeat(auto-fit, minmax(min(320px, 100%), 1fr));
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
overflow: hidden;
|
overflow: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card {
|
.card {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 1.5rem;
|
gap: 1.5rem;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
overflow: hidden;
|
overflow: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
.setting-row {
|
.setting-row {
|
||||||
@@ -311,7 +311,7 @@
|
|||||||
transition:
|
transition:
|
||||||
opacity 0.15s,
|
opacity 0.15s,
|
||||||
visibility 0.15s;
|
visibility 0.15s;
|
||||||
z-index: 1100;
|
z-index: 12000;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -329,7 +329,7 @@
|
|||||||
transition:
|
transition:
|
||||||
opacity 0.15s,
|
opacity 0.15s,
|
||||||
visibility 0.15s;
|
visibility 0.15s;
|
||||||
z-index: 1101;
|
z-index: 12001;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Tooltip aligned to left edge of icon (prevents clipping inside modals) */
|
/* Tooltip aligned to left edge of icon (prevents clipping inside modals) */
|
||||||
@@ -507,6 +507,20 @@
|
|||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.timezone-status {
|
||||||
|
min-height: 1.25rem;
|
||||||
|
margin-top: 8px;
|
||||||
|
margin-bottom: 0;
|
||||||
|
padding: 0;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
color: transparent;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timezone-status-saved {
|
||||||
|
color: var(--success);
|
||||||
|
}
|
||||||
|
|
||||||
/* Notification Matrix Mobile */
|
/* Notification Matrix Mobile */
|
||||||
@media (max-width: 480px) {
|
@media (max-width: 480px) {
|
||||||
.notification-matrix {
|
.notification-matrix {
|
||||||
|
|||||||
Reference in New Issue
Block a user