diff --git a/apps/citycorners/apps/backend/src/__tests__/mock-factories.ts b/apps/citycorners/apps/backend/src/__tests__/mock-factories.ts index 84a58ef58..003d21817 100644 --- a/apps/citycorners/apps/backend/src/__tests__/mock-factories.ts +++ b/apps/citycorners/apps/backend/src/__tests__/mock-factories.ts @@ -1,5 +1,6 @@ import type { Location } from '../db/schema/locations.schema'; import type { Favorite } from '../db/schema/favorites.schema'; +import type { Collection } from '../db/schema/collections.schema'; export const TEST_USER_ID = 'test-user-123'; export const TEST_USER_EMAIL = 'test@example.com'; @@ -8,6 +9,7 @@ export function createMockLocation(overrides: Partial = {}): Location return { id: 'loc-1', name: 'Konstanzer Münster', + slug: 'konstanzer-muenster', category: 'sight', description: 'Historic cathedral in Konstanz.', address: 'Münsterplatz 1, 78462 Konstanz', @@ -16,9 +18,13 @@ export function createMockLocation(overrides: Partial = {}): Location imageUrl: '/images/muenster.svg', images: [], timeline: [{ year: '615', event: 'Founded' }], + website: null, + phone: null, + openingHours: null, createdBy: null, createdAt: new Date('2026-01-01'), updatedAt: new Date('2026-01-01'), + deletedAt: null, ...overrides, }; } @@ -33,6 +39,19 @@ export function createMockFavorite(overrides: Partial = {}): Favorite }; } +export function createMockCollection(overrides: Partial = {}): Collection { + return { + id: 'col-1', + userId: TEST_USER_ID, + name: 'My Favorites', + description: null, + locationIds: [], + createdAt: new Date('2026-01-01'), + updatedAt: new Date('2026-01-01'), + ...overrides, + }; +} + export function createMockDb() { return { select: jest.fn().mockReturnThis(), diff --git a/apps/citycorners/apps/backend/src/app.module.ts b/apps/citycorners/apps/backend/src/app.module.ts index f4d0110a5..6ae202f9d 100644 --- a/apps/citycorners/apps/backend/src/app.module.ts +++ b/apps/citycorners/apps/backend/src/app.module.ts @@ -3,6 +3,7 @@ import { ConfigModule } from '@nestjs/config'; import { DatabaseModule } from './db/database.module'; import { LocationModule } from './location/location.module'; import { FavoriteModule } from './favorite/favorite.module'; +import { CollectionModule } from './collection/collection.module'; import { HealthModule } from '@manacore/shared-nestjs-health'; import { MetricsModule } from '@manacore/shared-nestjs-metrics'; @@ -15,6 +16,7 @@ import { MetricsModule } from '@manacore/shared-nestjs-metrics'; DatabaseModule, LocationModule, FavoriteModule, + CollectionModule, HealthModule.forRoot({ serviceName: 'citycorners-backend' }), MetricsModule.register({ prefix: 'citycorners_', diff --git a/apps/citycorners/apps/backend/src/collection/collection.controller.ts b/apps/citycorners/apps/backend/src/collection/collection.controller.ts new file mode 100644 index 000000000..47d80d7f6 --- /dev/null +++ b/apps/citycorners/apps/backend/src/collection/collection.controller.ts @@ -0,0 +1,82 @@ +import { Controller, Get, Post, Put, Delete, Body, Param, UseGuards } from '@nestjs/common'; +import { JwtAuthGuard, CurrentUser, CurrentUserData } from '@manacore/shared-nestjs-auth'; +import { CollectionService } from './collection.service'; +import { IsString, IsNotEmpty, IsOptional } from 'class-validator'; + +class CreateCollectionDto { + @IsString() + @IsNotEmpty() + name!: string; + + @IsString() + @IsOptional() + description?: string; +} + +class UpdateCollectionDto { + @IsString() + @IsOptional() + name?: string; + + @IsString() + @IsOptional() + description?: string; +} + +@Controller('collections') +@UseGuards(JwtAuthGuard) +export class CollectionController { + constructor(private readonly collectionService: CollectionService) {} + + @Get() + async findAll(@CurrentUser() user: CurrentUserData) { + const collections = await this.collectionService.findByUserId(user.userId); + return { collections }; + } + + @Post() + async create(@CurrentUser() user: CurrentUserData, @Body() dto: CreateCollectionDto) { + const collection = await this.collectionService.create({ + name: dto.name, + description: dto.description, + userId: user.userId, + }); + return { collection }; + } + + @Put(':id') + async update( + @CurrentUser() user: CurrentUserData, + @Param('id') id: string, + @Body() dto: UpdateCollectionDto + ) { + const collection = await this.collectionService.update(id, dto, user.userId); + return { collection }; + } + + @Delete(':id') + async delete(@CurrentUser() user: CurrentUserData, @Param('id') id: string) { + await this.collectionService.delete(id, user.userId); + return { success: true }; + } + + @Post(':id/locations/:locationId') + async addLocation( + @CurrentUser() user: CurrentUserData, + @Param('id') id: string, + @Param('locationId') locationId: string + ) { + const collection = await this.collectionService.addLocation(id, locationId, user.userId); + return { collection }; + } + + @Delete(':id/locations/:locationId') + async removeLocation( + @CurrentUser() user: CurrentUserData, + @Param('id') id: string, + @Param('locationId') locationId: string + ) { + const collection = await this.collectionService.removeLocation(id, locationId, user.userId); + return { collection }; + } +} diff --git a/apps/citycorners/apps/backend/src/collection/collection.module.ts b/apps/citycorners/apps/backend/src/collection/collection.module.ts new file mode 100644 index 000000000..e12282c73 --- /dev/null +++ b/apps/citycorners/apps/backend/src/collection/collection.module.ts @@ -0,0 +1,10 @@ +import { Module } from '@nestjs/common'; +import { CollectionController } from './collection.controller'; +import { CollectionService } from './collection.service'; + +@Module({ + controllers: [CollectionController], + providers: [CollectionService], + exports: [CollectionService], +}) +export class CollectionModule {} diff --git a/apps/citycorners/apps/backend/src/collection/collection.service.ts b/apps/citycorners/apps/backend/src/collection/collection.service.ts new file mode 100644 index 000000000..517932ce0 --- /dev/null +++ b/apps/citycorners/apps/backend/src/collection/collection.service.ts @@ -0,0 +1,87 @@ +import { Injectable, Inject, NotFoundException, ForbiddenException } from '@nestjs/common'; +import { eq, and } from 'drizzle-orm'; +import { DATABASE_CONNECTION } from '../db/database.module'; +import { Database } from '../db/connection'; +import { collections } from '../db/schema'; +import type { Collection, NewCollection } from '../db/schema'; + +@Injectable() +export class CollectionService { + constructor(@Inject(DATABASE_CONNECTION) private db: Database) {} + + async findByUserId(userId: string): Promise { + return this.db.select().from(collections).where(eq(collections.userId, userId)); + } + + async findById(id: string, userId: string): Promise { + const [collection] = await this.db + .select() + .from(collections) + .where(and(eq(collections.id, id), eq(collections.userId, userId))); + if (!collection) { + throw new NotFoundException(`Collection with id ${id} not found`); + } + return collection; + } + + async create(data: { name: string; description?: string; userId: string }): Promise { + const [collection] = await this.db + .insert(collections) + .values({ + name: data.name, + description: data.description, + userId: data.userId, + locationIds: [], + }) + .returning(); + return collection; + } + + async update( + id: string, + data: { name?: string; description?: string }, + userId: string + ): Promise { + const existing = await this.findById(id, userId); + + const [updated] = await this.db + .update(collections) + .set(data) + .where(eq(collections.id, id)) + .returning(); + return updated; + } + + async delete(id: string, userId: string): Promise { + await this.findById(id, userId); + await this.db.delete(collections).where(eq(collections.id, id)); + } + + async addLocation(id: string, locationId: string, userId: string): Promise { + const collection = await this.findById(id, userId); + const currentIds: string[] = (collection.locationIds as string[]) || []; + + if (currentIds.includes(locationId)) { + return collection; + } + + const [updated] = await this.db + .update(collections) + .set({ locationIds: [...currentIds, locationId] }) + .where(eq(collections.id, id)) + .returning(); + return updated; + } + + async removeLocation(id: string, locationId: string, userId: string): Promise { + const collection = await this.findById(id, userId); + const currentIds: string[] = (collection.locationIds as string[]) || []; + + const [updated] = await this.db + .update(collections) + .set({ locationIds: currentIds.filter((lid) => lid !== locationId) }) + .where(eq(collections.id, id)) + .returning(); + return updated; + } +} diff --git a/apps/citycorners/apps/backend/src/db/schema/collections.schema.ts b/apps/citycorners/apps/backend/src/db/schema/collections.schema.ts new file mode 100644 index 000000000..325621dc3 --- /dev/null +++ b/apps/citycorners/apps/backend/src/db/schema/collections.schema.ts @@ -0,0 +1,17 @@ +import { pgTable, uuid, text, timestamp, jsonb } from 'drizzle-orm/pg-core'; + +export const collections = pgTable('collections', { + id: uuid('id').primaryKey().defaultRandom(), + userId: text('user_id').notNull(), + name: text('name').notNull(), + description: text('description'), + locationIds: jsonb('location_ids').$type().default([]), + createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(), + updatedAt: timestamp('updated_at', { withTimezone: true }) + .defaultNow() + .$onUpdate(() => new Date()) + .notNull(), +}); + +export type Collection = typeof collections.$inferSelect; +export type NewCollection = typeof collections.$inferInsert; diff --git a/apps/citycorners/apps/backend/src/db/schema/index.ts b/apps/citycorners/apps/backend/src/db/schema/index.ts index e1f03c671..01100f11d 100644 --- a/apps/citycorners/apps/backend/src/db/schema/index.ts +++ b/apps/citycorners/apps/backend/src/db/schema/index.ts @@ -1,2 +1,3 @@ export * from './locations.schema'; export * from './favorites.schema'; +export * from './collections.schema'; diff --git a/apps/citycorners/apps/backend/src/db/schema/locations.schema.ts b/apps/citycorners/apps/backend/src/db/schema/locations.schema.ts index 6c1bed490..98a1cee2e 100644 --- a/apps/citycorners/apps/backend/src/db/schema/locations.schema.ts +++ b/apps/citycorners/apps/backend/src/db/schema/locations.schema.ts @@ -10,9 +10,12 @@ import { export const categoryEnum = pgEnum('location_category', ['sight', 'restaurant', 'shop', 'museum']); +export type OpeningHours = Record; + export const locations = pgTable('locations', { id: uuid('id').primaryKey().defaultRandom(), name: text('name').notNull(), + slug: text('slug').unique(), category: categoryEnum('category').notNull(), description: text('description').notNull(), address: text('address'), @@ -21,12 +24,16 @@ export const locations = pgTable('locations', { imageUrl: text('image_url'), images: jsonb('images').$type().default([]), timeline: jsonb('timeline').$type().default([]), + website: text('website'), + phone: text('phone'), + openingHours: jsonb('opening_hours').$type(), createdBy: text('created_by'), createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(), updatedAt: timestamp('updated_at', { withTimezone: true }) .defaultNow() .$onUpdate(() => new Date()) .notNull(), + deletedAt: timestamp('deleted_at', { withTimezone: true }), }); export interface LocationImage { diff --git a/apps/citycorners/apps/backend/src/db/seed.ts b/apps/citycorners/apps/backend/src/db/seed.ts index 2abb30ead..78eb885f6 100644 --- a/apps/citycorners/apps/backend/src/db/seed.ts +++ b/apps/citycorners/apps/backend/src/db/seed.ts @@ -17,6 +17,7 @@ async function seed() { await db.insert(locations).values([ { name: 'Konstanzer Münster', + slug: 'konstanzer-muenster', category: 'sight', description: 'Das Konstanzer Münster ist eine römisch-katholische Basilika in der Altstadt von Konstanz. Der Bau begann im Jahr 615 und wurde im Laufe der Jahrhunderte mehrmals erweitert.', @@ -32,6 +33,7 @@ async function seed() { }, { name: 'Imperia', + slug: 'imperia', category: 'sight', description: 'Die Imperia ist eine satirische Skulptur des Bildhauers Peter Lenk im Hafen von Konstanz. Sie dreht sich langsam um die eigene Achse.', @@ -43,6 +45,7 @@ async function seed() { }, { name: 'Restaurant Ophelia', + slug: 'restaurant-ophelia', category: 'restaurant', description: 'Fine-Dining-Restaurant im Riva-Gebäude am Konstanzer Hafen mit Blick auf den Bodensee.', @@ -53,6 +56,7 @@ async function seed() { }, { name: 'LAGO Shopping Center', + slug: 'lago-shopping-center', category: 'shop', description: 'Großes Einkaufszentrum in der Konstanzer Innenstadt mit über 80 Geschäften.', address: 'Bodanstraße 1, 78462 Konstanz', @@ -62,6 +66,7 @@ async function seed() { }, { name: 'Rosgartenmuseum', + slug: 'rosgartenmuseum', category: 'museum', description: 'Das Rosgartenmuseum zeigt die Geschichte der Stadt Konstanz und der Bodenseeregion.', @@ -71,6 +76,7 @@ async function seed() { }, { name: 'Archäologisches Landesmuseum', + slug: 'archaeologisches-landesmuseum', category: 'museum', description: 'Landesmuseum für Archäologie in Baden-Württemberg mit Funden aus der Region.', address: 'Benediktinerplatz 5, 78467 Konstanz', diff --git a/apps/citycorners/apps/backend/src/guards/rate-limit.guard.ts b/apps/citycorners/apps/backend/src/guards/rate-limit.guard.ts new file mode 100644 index 000000000..40f37a316 --- /dev/null +++ b/apps/citycorners/apps/backend/src/guards/rate-limit.guard.ts @@ -0,0 +1,68 @@ +import { + Injectable, + CanActivate, + ExecutionContext, + HttpException, + HttpStatus, +} from '@nestjs/common'; + +interface RequestRecord { + count: number; + resetAt: number; +} + +@Injectable() +export class RateLimitGuard implements CanActivate { + private readonly requests = new Map(); + private readonly maxRequests = 10; + private readonly windowMs = 60_000; // 1 minute + private cleanupInterval: ReturnType; + + constructor() { + // Clean up old entries every 5 minutes + this.cleanupInterval = setInterval(() => this.cleanup(), 5 * 60_000); + this.cleanupInterval.unref(); + } + + canActivate(context: ExecutionContext): boolean { + const request = context.switchToHttp().getRequest(); + const ip = + request.headers['x-forwarded-for']?.split(',')[0]?.trim() || + request.ip || + request.connection?.remoteAddress || + 'unknown'; + + const now = Date.now(); + const record = this.requests.get(ip); + + if (!record || now > record.resetAt) { + this.requests.set(ip, { count: 1, resetAt: now + this.windowMs }); + return true; + } + + record.count++; + + if (record.count > this.maxRequests) { + const retryAfter = Math.ceil((record.resetAt - now) / 1000); + throw new HttpException( + { + statusCode: HttpStatus.TOO_MANY_REQUESTS, + message: 'Too many requests. Please try again later.', + retryAfter, + }, + HttpStatus.TOO_MANY_REQUESTS + ); + } + + return true; + } + + private cleanup() { + const now = Date.now(); + for (const [ip, record] of this.requests) { + if (now > record.resetAt) { + this.requests.delete(ip); + } + } + } +} diff --git a/apps/citycorners/apps/backend/src/location/location.controller.spec.ts b/apps/citycorners/apps/backend/src/location/location.controller.spec.ts index 2756cd01f..57ecf892a 100644 --- a/apps/citycorners/apps/backend/src/location/location.controller.spec.ts +++ b/apps/citycorners/apps/backend/src/location/location.controller.spec.ts @@ -12,10 +12,13 @@ describe('LocationController', () => { locationService = { findAll: jest.fn(), findById: jest.fn(), + findBySlug: jest.fn(), + findByIdOrSlug: jest.fn(), search: jest.fn(), create: jest.fn(), update: jest.fn(), delete: jest.fn(), + restore: jest.fn(), }; lookupService = { lookup: jest.fn(), @@ -57,6 +60,18 @@ describe('LocationController', () => { }); }); + describe('findById', () => { + it('should use findByIdOrSlug', async () => { + const location = createMockLocation(); + locationService.findByIdOrSlug.mockResolvedValue(location); + + const result = await controller.findById('konstanzer-muenster'); + + expect(locationService.findByIdOrSlug).toHaveBeenCalledWith('konstanzer-muenster'); + expect(result).toEqual({ location }); + }); + }); + describe('search', () => { it('should search locations', async () => { const locations = [createMockLocation()]; @@ -142,4 +157,16 @@ describe('LocationController', () => { expect(locationService.delete).toHaveBeenCalledWith('loc-1', TEST_USER_ID); }); }); + + describe('restore', () => { + it('should restore a soft-deleted location', async () => { + const location = createMockLocation(); + locationService.restore.mockResolvedValue(location); + + const result = await controller.restore(mockUser, 'loc-1'); + + expect(result).toEqual({ location }); + expect(locationService.restore).toHaveBeenCalledWith('loc-1', TEST_USER_ID); + }); + }); }); diff --git a/apps/citycorners/apps/backend/src/location/location.controller.ts b/apps/citycorners/apps/backend/src/location/location.controller.ts index e2e4fa24d..811f8dd10 100644 --- a/apps/citycorners/apps/backend/src/location/location.controller.ts +++ b/apps/citycorners/apps/backend/src/location/location.controller.ts @@ -2,8 +2,10 @@ import { Controller, Get, Post, Put, Delete, Body, Param, Query, UseGuards } fro import { JwtAuthGuard, CurrentUser, CurrentUserData } from '@manacore/shared-nestjs-auth'; import { LocationService } from './location.service'; import { LocationLookupService } from './location-lookup.service'; -import { IsString, IsNotEmpty, IsOptional, IsNumber } from 'class-validator'; +import { RateLimitGuard } from '../guards/rate-limit.guard'; +import { IsString, IsNotEmpty, IsOptional, IsNumber, IsObject } from 'class-validator'; import { Type } from 'class-transformer'; +import type { OpeningHours } from '../db/schema/locations.schema'; class CreateLocationDto { @IsString() @@ -35,6 +37,18 @@ class CreateLocationDto { @IsString() @IsOptional() imageUrl?: string; + + @IsString() + @IsOptional() + website?: string; + + @IsString() + @IsOptional() + phone?: string; + + @IsObject() + @IsOptional() + openingHours?: OpeningHours; } class UpdateLocationDto { @@ -67,6 +81,18 @@ class UpdateLocationDto { @IsString() @IsOptional() imageUrl?: string; + + @IsString() + @IsOptional() + website?: string; + + @IsString() + @IsOptional() + phone?: string; + + @IsObject() + @IsOptional() + openingHours?: OpeningHours; } @Controller('locations') @@ -126,7 +152,7 @@ export class LocationController { @Get(':id') async findById(@Param('id') id: string) { - const location = await this.locationService.findById(id); + const location = await this.locationService.findByIdOrSlug(id); return { location }; } @@ -149,7 +175,7 @@ export class LocationController { } @Post() - @UseGuards(JwtAuthGuard) + @UseGuards(JwtAuthGuard, RateLimitGuard) async create(@CurrentUser() user: CurrentUserData, @Body() dto: CreateLocationDto) { const location = await this.locationService.create({ ...dto, @@ -159,7 +185,7 @@ export class LocationController { } @Put(':id') - @UseGuards(JwtAuthGuard) + @UseGuards(JwtAuthGuard, RateLimitGuard) async update( @CurrentUser() user: CurrentUserData, @Param('id') id: string, @@ -170,9 +196,16 @@ export class LocationController { } @Delete(':id') - @UseGuards(JwtAuthGuard) + @UseGuards(JwtAuthGuard, RateLimitGuard) async delete(@CurrentUser() user: CurrentUserData, @Param('id') id: string) { await this.locationService.delete(id, user.userId); return { success: true }; } + + @Post(':id/restore') + @UseGuards(JwtAuthGuard) + async restore(@CurrentUser() user: CurrentUserData, @Param('id') id: string) { + const location = await this.locationService.restore(id, user.userId); + return { location }; + } } diff --git a/apps/citycorners/apps/backend/src/location/location.service.spec.ts b/apps/citycorners/apps/backend/src/location/location.service.spec.ts index f49226524..f5a2437d1 100644 --- a/apps/citycorners/apps/backend/src/location/location.service.spec.ts +++ b/apps/citycorners/apps/backend/src/location/location.service.spec.ts @@ -1,6 +1,6 @@ import { Test, TestingModule } from '@nestjs/testing'; import { NotFoundException, ForbiddenException } from '@nestjs/common'; -import { LocationService } from './location.service'; +import { LocationService, generateSlug } from './location.service'; import { DATABASE_CONNECTION } from '../db/database.module'; import { createMockDb, createMockLocation } from '../__tests__/mock-factories'; @@ -20,16 +20,36 @@ describe('LocationService', () => { afterEach(() => jest.clearAllMocks()); + describe('generateSlug', () => { + it('should convert name to slug', () => { + expect(generateSlug('Konstanzer Münster')).toBe('konstanzer-muenster'); + }); + + it('should replace umlauts', () => { + expect(generateSlug('Über den Flüssen')).toBe('ueber-den-fluessen'); + }); + + it('should replace ß with ss', () => { + expect(generateSlug('Große Straße')).toBe('grosse-strasse'); + }); + + it('should deduplicate hyphens', () => { + expect(generateSlug('Name -- with --- hyphens')).toBe('name-with-hyphens'); + }); + + it('should trim leading/trailing hyphens', () => { + expect(generateSlug(' Hello World ')).toBe('hello-world'); + }); + }); + describe('findAll', () => { it('should return paginated locations', async () => { const locations = [ createMockLocation(), createMockLocation({ id: 'loc-2', name: 'Imperia' }), ]; - // Without category: count calls from() which resolves, data calls offset() - mockDb.from - .mockResolvedValueOnce([{ count: 2 }]) // count query - .mockReturnThis(); // data query chain continues + // Without category: count query calls where() (for notDeleted filter), data calls offset() + mockDb.where.mockResolvedValueOnce([{ count: 2 }]); // count query mockDb.offset.mockResolvedValue(locations); const result = await service.findAll(); @@ -56,7 +76,7 @@ describe('LocationService', () => { }); it('should respect page and limit', async () => { - mockDb.from.mockResolvedValueOnce([{ count: 50 }]).mockReturnThis(); + mockDb.where.mockResolvedValueOnce([{ count: 50 }]); mockDb.offset.mockResolvedValue([]); const result = await service.findAll(undefined, 3, 10); @@ -84,6 +104,43 @@ describe('LocationService', () => { }); }); + describe('findBySlug', () => { + it('should return a location by slug', async () => { + const location = createMockLocation(); + mockDb.where.mockResolvedValue([location]); + + const result = await service.findBySlug('konstanzer-muenster'); + + expect(result).toEqual(location); + }); + + it('should throw NotFoundException if slug not found', async () => { + mockDb.where.mockResolvedValue([]); + + await expect(service.findBySlug('nonexistent-slug')).rejects.toThrow(NotFoundException); + }); + }); + + describe('findByIdOrSlug', () => { + it('should call findById for UUID', async () => { + const location = createMockLocation(); + mockDb.where.mockResolvedValue([location]); + + const result = await service.findByIdOrSlug('a1b2c3d4-e5f6-7890-abcd-ef1234567890'); + + expect(result).toEqual(location); + }); + + it('should call findBySlug for non-UUID', async () => { + const location = createMockLocation(); + mockDb.where.mockResolvedValue([location]); + + const result = await service.findByIdOrSlug('konstanzer-muenster'); + + expect(result).toEqual(location); + }); + }); + describe('search', () => { it('should search locations by query', async () => { const locations = [createMockLocation()]; @@ -104,8 +161,10 @@ describe('LocationService', () => { }); describe('create', () => { - it('should create a new location', async () => { + it('should create a new location with auto-generated slug', async () => { const newLocation = createMockLocation({ id: 'loc-new' }); + // generateUniqueSlug: check existing slug + mockDb.where.mockResolvedValueOnce([]); // no existing slug mockDb.returning.mockResolvedValue([newLocation]); const result = await service.create({ @@ -161,11 +220,15 @@ describe('LocationService', () => { }); describe('delete', () => { - it('should delete a location owned by user', async () => { + it('should soft delete a location owned by user', async () => { const existing = createMockLocation({ createdBy: 'user-1' }); - mockDb.where.mockResolvedValueOnce([existing]); // findById + mockDb.where + .mockResolvedValueOnce([existing]) // findById + .mockReturnThis(); // update where await expect(service.delete('loc-1', 'user-1')).resolves.not.toThrow(); + expect(mockDb.update).toHaveBeenCalled(); + expect(mockDb.set).toHaveBeenCalled(); }); it('should throw ForbiddenException if not owner', async () => { @@ -181,4 +244,36 @@ describe('LocationService', () => { await expect(service.delete('nonexistent')).rejects.toThrow(NotFoundException); }); }); + + describe('restore', () => { + it('should restore a soft-deleted location', async () => { + const deleted = createMockLocation({ + createdBy: 'user-1', + deletedAt: new Date(), + }); + mockDb.where.mockResolvedValueOnce([deleted]); // find + const restored = createMockLocation({ createdBy: 'user-1', deletedAt: null }); + mockDb.returning.mockResolvedValue([restored]); + + const result = await service.restore('loc-1', 'user-1'); + + expect(result.deletedAt).toBeNull(); + }); + + it('should throw ForbiddenException if not owner', async () => { + const deleted = createMockLocation({ + createdBy: 'other-user', + deletedAt: new Date(), + }); + mockDb.where.mockResolvedValueOnce([deleted]); + + await expect(service.restore('loc-1', 'attacker-user')).rejects.toThrow(ForbiddenException); + }); + + it('should throw NotFoundException if not found', async () => { + mockDb.where.mockResolvedValue([]); + + await expect(service.restore('nonexistent')).rejects.toThrow(NotFoundException); + }); + }); }); diff --git a/apps/citycorners/apps/backend/src/location/location.service.ts b/apps/citycorners/apps/backend/src/location/location.service.ts index e38800c66..570eb3a06 100644 --- a/apps/citycorners/apps/backend/src/location/location.service.ts +++ b/apps/citycorners/apps/backend/src/location/location.service.ts @@ -1,5 +1,5 @@ import { Injectable, Inject, NotFoundException, ForbiddenException } from '@nestjs/common'; -import { eq, or, ilike, sql, desc, ne, and, isNotNull } from 'drizzle-orm'; +import { eq, or, ilike, sql, desc, ne, and, isNotNull, isNull } from 'drizzle-orm'; import { DATABASE_CONNECTION } from '../db/database.module'; import { Database } from '../db/connection'; import { locations } from '../db/schema'; @@ -13,10 +13,26 @@ export interface PaginatedResult { totalPages: number; } +export function generateSlug(name: string): string { + return name + .toLowerCase() + .replace(/ä/g, 'ae') + .replace(/ö/g, 'oe') + .replace(/ü/g, 'ue') + .replace(/ß/g, 'ss') + .replace(/[^a-z0-9]+/g, '-') + .replace(/-+/g, '-') + .replace(/^-|-$/g, ''); +} + +const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; + @Injectable() export class LocationService { constructor(@Inject(DATABASE_CONNECTION) private db: Database) {} + private notDeleted = isNull(locations.deletedAt); + async findAll(category?: string, page = 1, limit = 20): Promise> { const offset = (page - 1) * limit; @@ -27,25 +43,27 @@ export class LocationService { const countResult = await this.db .select({ count: sql`count(*)::int` }) .from(locations) - .where(eq(locations.category, category as Location['category'])); + .where(and(eq(locations.category, category as Location['category']), this.notDeleted)); total = countResult[0]?.count ?? 0; items = await this.db .select() .from(locations) - .where(eq(locations.category, category as Location['category'])) + .where(and(eq(locations.category, category as Location['category']), this.notDeleted)) .orderBy(desc(locations.createdAt)) .limit(limit) .offset(offset); } else { const countResult = await this.db .select({ count: sql`count(*)::int` }) - .from(locations); + .from(locations) + .where(this.notDeleted); total = countResult[0]?.count ?? 0; items = await this.db .select() .from(locations) + .where(this.notDeleted) .orderBy(desc(locations.createdAt)) .limit(limit) .offset(offset); @@ -66,27 +84,73 @@ export class LocationService { .select() .from(locations) .where( - or( - ilike(locations.name, pattern), - ilike(locations.description, pattern), - ilike(locations.address, pattern) + and( + or( + ilike(locations.name, pattern), + ilike(locations.description, pattern), + ilike(locations.address, pattern) + ), + this.notDeleted ) ); } async findById(id: string): Promise { - const [location] = await this.db.select().from(locations).where(eq(locations.id, id)); + const [location] = await this.db + .select() + .from(locations) + .where(and(eq(locations.id, id), this.notDeleted)); if (!location) { throw new NotFoundException(`Location with id ${id} not found`); } return location; } - async create(data: NewLocation): Promise { - const [location] = await this.db.insert(locations).values(data).returning(); + async findBySlug(slug: string): Promise { + const [location] = await this.db + .select() + .from(locations) + .where(and(eq(locations.slug, slug), this.notDeleted)); + if (!location) { + throw new NotFoundException(`Location with slug ${slug} not found`); + } return location; } + async findByIdOrSlug(idOrSlug: string): Promise { + if (UUID_PATTERN.test(idOrSlug)) { + return this.findById(idOrSlug); + } + return this.findBySlug(idOrSlug); + } + + async create(data: NewLocation): Promise { + const slug = await this.generateUniqueSlug(data.name); + const [location] = await this.db + .insert(locations) + .values({ ...data, slug }) + .returning(); + return location; + } + + private async generateUniqueSlug(name: string): Promise { + const baseSlug = generateSlug(name); + let slug = baseSlug; + let counter = 1; + + while (true) { + const [existing] = await this.db + .select({ id: locations.id }) + .from(locations) + .where(eq(locations.slug, slug)); + if (!existing) break; + counter++; + slug = `${baseSlug}-${counter}`; + } + + return slug; + } + async update(id: string, data: Partial, userId?: string): Promise { const existing = await this.findById(id); @@ -126,7 +190,12 @@ export class LocationService { }) .from(locations) .where( - and(ne(locations.id, id), isNotNull(locations.latitude), isNotNull(locations.longitude)) + and( + ne(locations.id, id), + isNotNull(locations.latitude), + isNotNull(locations.longitude), + this.notDeleted + ) ) .orderBy(haversine) .limit(limit); @@ -166,7 +235,7 @@ export class LocationService { const results = await this.db .select({ id: locations.id, name: locations.name, category: locations.category }) .from(locations) - .where(ilike(locations.name, pattern)) + .where(and(ilike(locations.name, pattern), this.notDeleted)) .limit(limit); return results; } @@ -179,6 +248,26 @@ export class LocationService { throw new ForbiddenException('You can only delete your own locations'); } - await this.db.delete(locations).where(eq(locations.id, id)); + // Soft delete + await this.db.update(locations).set({ deletedAt: new Date() }).where(eq(locations.id, id)); + } + + async restore(id: string, userId?: string): Promise { + // Find including soft-deleted + const [existing] = await this.db.select().from(locations).where(eq(locations.id, id)); + if (!existing) { + throw new NotFoundException(`Location with id ${id} not found`); + } + + if (existing.createdBy && userId && existing.createdBy !== userId) { + throw new ForbiddenException('You can only restore your own locations'); + } + + const [restored] = await this.db + .update(locations) + .set({ deletedAt: null }) + .where(eq(locations.id, id)) + .returning(); + return restored; } } diff --git a/apps/citycorners/apps/landing/src/components/LocationCard.astro b/apps/citycorners/apps/landing/src/components/LocationCard.astro index 49cdd8961..3e21313d0 100644 --- a/apps/citycorners/apps/landing/src/components/LocationCard.astro +++ b/apps/citycorners/apps/landing/src/components/LocationCard.astro @@ -1,7 +1,7 @@ --- interface Props { location: { - id: number; + id: number | string; name: string; category: string; description: string; diff --git a/apps/citycorners/apps/landing/src/pages/index.astro b/apps/citycorners/apps/landing/src/pages/index.astro index 4d650556a..0ef57b25b 100644 --- a/apps/citycorners/apps/landing/src/pages/index.astro +++ b/apps/citycorners/apps/landing/src/pages/index.astro @@ -2,7 +2,39 @@ import Layout from '../layouts/Layout.astro'; import LocationCard from '../components/LocationCard.astro'; import Filter from '../components/Filter.astro'; -import locations from '../data/locations.json'; +import fallbackLocations from '../data/locations.json'; + +const BACKEND_URL = import.meta.env.BACKEND_URL || 'http://localhost:3025'; + +let locations = fallbackLocations; + +try { + const res = await fetch(`${BACKEND_URL}/api/v1/locations?limit=100`); + if (res.ok) { + const data = await res.json(); + // Map API response to the shape expected by LocationCard + const categoryMap: Record = { + sight: 'Sehenswürdigkeit', + restaurant: 'Restaurant', + shop: 'Laden', + museum: 'Museum', + }; + locations = data.locations.map((loc: any, index: number) => ({ + id: loc.slug || loc.id, + name: loc.name, + category: categoryMap[loc.category] || loc.category, + description: loc.description, + image: loc.imageUrl || '/images/placeholder.jpg', + address: loc.address, + coordinates: + loc.latitude && loc.longitude ? { lat: loc.latitude, lng: loc.longitude } : undefined, + timeline: loc.timeline?.map((t: any) => ({ year: t.year, description: t.event })), + })); + } +} catch (e) { + // Fallback to hardcoded JSON if API is unreachable + console.warn('Could not fetch from API, using fallback data:', e); +} --- diff --git a/apps/citycorners/apps/landing/src/pages/locations/[id].astro b/apps/citycorners/apps/landing/src/pages/locations/[id].astro index 743fa7f15..afff698c2 100644 --- a/apps/citycorners/apps/landing/src/pages/locations/[id].astro +++ b/apps/citycorners/apps/landing/src/pages/locations/[id].astro @@ -1,19 +1,15 @@ --- import Layout from '../../layouts/Layout.astro'; -import locations from '../../data/locations.json'; +import fallbackLocations from '../../data/locations.json'; -export async function getStaticPaths() { - return locations.map((location) => ({ - params: { id: location.id.toString() }, - })); -} +const BACKEND_URL = import.meta.env.BACKEND_URL || 'http://localhost:3025'; -const { id } = Astro.params; -const location = locations.find((loc) => loc.id.toString() === id); - -if (!location) { - return Astro.redirect('/'); -} +const categoryMap: Record = { + sight: 'Sehenswürdigkeit', + restaurant: 'Restaurant', + shop: 'Laden', + museum: 'Museum', +}; const categoryColors: Record = { Sehenswürdigkeit: 'bg-blue-100 text-blue-700', @@ -21,6 +17,68 @@ const categoryColors: Record = { Laden: 'bg-green-100 text-green-700', Museum: 'bg-purple-100 text-purple-700', }; + +function mapApiLocation(loc: any) { + return { + id: loc.slug || loc.id, + name: loc.name, + category: categoryMap[loc.category] || loc.category, + description: loc.description, + image: loc.imageUrl || '/images/placeholder.jpg', + address: loc.address, + coordinates: + loc.latitude && loc.longitude ? { lat: loc.latitude, lng: loc.longitude } : undefined, + timeline: loc.timeline?.map((t: any) => ({ year: t.year, description: t.event })), + }; +} + +let allLocations: any[] = []; + +try { + const res = await fetch(`${BACKEND_URL}/api/v1/locations?limit=100`); + if (res.ok) { + const data = await res.json(); + allLocations = data.locations.map(mapApiLocation); + } else { + allLocations = fallbackLocations; + } +} catch (e) { + console.warn('Could not fetch from API, using fallback data:', e); + allLocations = fallbackLocations; +} + +export async function getStaticPaths() { + let locations: any[] = []; + + try { + const backendUrl = import.meta.env.BACKEND_URL || 'http://localhost:3025'; + const res = await fetch(`${backendUrl}/api/v1/locations?limit=100`); + if (res.ok) { + const data = await res.json(); + locations = data.locations.map((loc: any) => ({ + id: loc.slug || loc.id, + })); + } + } catch (e) { + // Fallback + } + + if (locations.length === 0) { + const fallback = await import('../../data/locations.json'); + locations = fallback.default.map((loc) => ({ id: loc.id.toString() })); + } + + return locations.map((loc) => ({ + params: { id: loc.id.toString() }, + })); +} + +const { id } = Astro.params; +const location = allLocations.find((loc) => loc.id.toString() === id); + +if (!location) { + return Astro.redirect('/'); +} --- diff --git a/apps/citycorners/apps/web/package.json b/apps/citycorners/apps/web/package.json index 96363cf0c..4ebeded99 100644 --- a/apps/citycorners/apps/web/package.json +++ b/apps/citycorners/apps/web/package.json @@ -27,7 +27,8 @@ "tailwindcss": "^4.1.7", "tslib": "^2.4.1", "typescript": "^5.9.3", - "vite": "^6.0.0" + "vite": "^6.0.0", + "vitest": "^4.1.1" }, "dependencies": { "@manacore/shared-auth": "workspace:*", diff --git a/apps/citycorners/apps/web/src/lib/api.test.ts b/apps/citycorners/apps/web/src/lib/api.test.ts new file mode 100644 index 000000000..b5b758d2e --- /dev/null +++ b/apps/citycorners/apps/web/src/lib/api.test.ts @@ -0,0 +1,52 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; + +// Mock $app/environment +vi.mock('$app/environment', () => ({ + browser: true, +})); + +describe('api utilities', () => { + beforeEach(() => { + vi.resetModules(); + // Clear any window overrides + delete (window as any).__PUBLIC_BACKEND_URL__; + }); + + describe('getBackendUrl', () => { + it('should use injected URL if available', async () => { + (window as any).__PUBLIC_BACKEND_URL__ = 'https://api.example.com'; + const { getBackendUrl } = await import('./api'); + expect(getBackendUrl()).toBe('https://api.example.com'); + }); + + it('should default to localhost:3025 in dev mode', async () => { + const { getBackendUrl } = await import('./api'); + // When no injected URL and import.meta.env.DEV is true + const url = getBackendUrl(); + // Either localhost or empty string depending on env + expect(typeof url).toBe('string'); + }); + }); + + describe('api', () => { + it('should prepend backend URL and /api/v1 prefix', async () => { + (window as any).__PUBLIC_BACKEND_URL__ = 'https://api.example.com'; + const { api } = await import('./api'); + expect(api('/locations')).toBe('https://api.example.com/api/v1/locations'); + }); + + it('should handle paths with query params', async () => { + (window as any).__PUBLIC_BACKEND_URL__ = 'https://api.example.com'; + const { api } = await import('./api'); + expect(api('/locations?category=sight')).toBe( + 'https://api.example.com/api/v1/locations?category=sight' + ); + }); + + it('should handle path with id parameter', async () => { + (window as any).__PUBLIC_BACKEND_URL__ = 'https://api.example.com'; + const { api } = await import('./api'); + expect(api('/locations/abc-123')).toBe('https://api.example.com/api/v1/locations/abc-123'); + }); + }); +}); diff --git a/apps/citycorners/apps/web/src/lib/i18n/locales/de.json b/apps/citycorners/apps/web/src/lib/i18n/locales/de.json index 680312f33..49968502e 100644 --- a/apps/citycorners/apps/web/src/lib/i18n/locales/de.json +++ b/apps/citycorners/apps/web/src/lib/i18n/locales/de.json @@ -50,7 +50,20 @@ "confirmDelete": "Endgültig löschen", "deleting": "Wird gelöscht...", "cancel": "Abbrechen", - "nearby": "In der Nähe" + "nearby": "In der Nähe", + "website": "Webseite", + "phone": "Telefon", + "openingHours": "Öffnungszeiten", + "closed": "Geschlossen" + }, + "days": { + "mo": "Montag", + "tu": "Dienstag", + "we": "Mittwoch", + "th": "Donnerstag", + "fr": "Freitag", + "sa": "Samstag", + "su": "Sonntag" }, "gallery": { "addPhoto": "Foto hinzufügen", @@ -63,7 +76,25 @@ "empty": "Noch keine Favoriten. Tippe auf das Herz bei einer Location, um sie zu speichern.", "loginRequired": "Melde dich an, um Favoriten zu speichern.", "add": "Zu Favoriten hinzufügen", - "remove": "Aus Favoriten entfernen" + "remove": "Aus Favoriten entfernen", + "tabFavorites": "Favoriten", + "tabCollections": "Sammlungen" + }, + "collections": { + "title": "Sammlungen", + "empty": "Noch keine Sammlungen erstellt.", + "create": "Sammlung erstellen", + "name": "Name", + "namePlaceholder": "z.B. Meine Lieblingsrestaurants", + "description": "Beschreibung (optional)", + "descriptionPlaceholder": "Worum geht es in dieser Sammlung?", + "save": "Speichern", + "cancel": "Abbrechen", + "locations": "{count} Orte", + "noLocations": "Keine Orte in dieser Sammlung.", + "delete": "Sammlung löschen", + "deleteConfirm": "Bist du sicher, dass du diese Sammlung löschen möchtest?", + "back": "Zurück zu Sammlungen" }, "map": { "title": "Karte", @@ -127,7 +158,11 @@ "imageLoadError": "Bild konnte nicht geladen werden.", "imageRetry": "Erneut versuchen", "geocoding": "Koordinaten werden ermittelt...", - "coordinatesFound": "Koordinaten gefunden" + "coordinatesFound": "Koordinaten gefunden", + "website": "Webseite (optional)", + "websitePlaceholder": "https://example.com", + "phone": "Telefon (optional)", + "phonePlaceholder": "+49 7531 12345" }, "edit": { "title": "Ort bearbeiten", diff --git a/apps/citycorners/apps/web/src/lib/i18n/locales/en.json b/apps/citycorners/apps/web/src/lib/i18n/locales/en.json index 0dd72e3c9..fd30ddb2d 100644 --- a/apps/citycorners/apps/web/src/lib/i18n/locales/en.json +++ b/apps/citycorners/apps/web/src/lib/i18n/locales/en.json @@ -50,7 +50,20 @@ "confirmDelete": "Delete permanently", "deleting": "Deleting...", "cancel": "Cancel", - "nearby": "Nearby" + "nearby": "Nearby", + "website": "Website", + "phone": "Phone", + "openingHours": "Opening hours", + "closed": "Closed" + }, + "days": { + "mo": "Monday", + "tu": "Tuesday", + "we": "Wednesday", + "th": "Thursday", + "fr": "Friday", + "sa": "Saturday", + "su": "Sunday" }, "gallery": { "addPhoto": "Add photo", @@ -63,7 +76,25 @@ "empty": "No favorites yet. Tap the heart on a location to save it.", "loginRequired": "Sign in to save favorites.", "add": "Add to favorites", - "remove": "Remove from favorites" + "remove": "Remove from favorites", + "tabFavorites": "Favorites", + "tabCollections": "Collections" + }, + "collections": { + "title": "Collections", + "empty": "No collections created yet.", + "create": "Create collection", + "name": "Name", + "namePlaceholder": "e.g. My favorite restaurants", + "description": "Description (optional)", + "descriptionPlaceholder": "What is this collection about?", + "save": "Save", + "cancel": "Cancel", + "locations": "{count} places", + "noLocations": "No places in this collection.", + "delete": "Delete collection", + "deleteConfirm": "Are you sure you want to delete this collection?", + "back": "Back to collections" }, "map": { "title": "Map", @@ -103,16 +134,16 @@ "title": "Add a place", "subtitle": "Share your favorite spot in Konstanz", "name": "Name", - "namePlaceholder": "e.g. Lakeside Café", + "namePlaceholder": "e.g. Lakeside Cafe", "category": "Category", "description": "Description", "descriptionPlaceholder": "What makes this place special?", "minChars": "At least 10 characters", "address": "Address (optional)", - "addressPlaceholder": "e.g. Seestraße 1, 78462 Konstanz", + "addressPlaceholder": "e.g. Seestrasse 1, 78462 Konstanz", "searchTitle": "Search for a place online", "searchSubtitle": "We'll automatically find info and pre-fill the form for you.", - "searchPlaceholder": "e.g. Café Zeitlos Konstanz", + "searchPlaceholder": "e.g. Cafe Zeitlos Konstanz", "searchButton": "Search", "skipSearch": "Skip and enter manually", "foundSources": "Sources found:", @@ -127,7 +158,11 @@ "imageLoadError": "Image could not be loaded.", "imageRetry": "Retry", "geocoding": "Finding coordinates...", - "coordinatesFound": "Coordinates found" + "coordinatesFound": "Coordinates found", + "website": "Website (optional)", + "websitePlaceholder": "https://example.com", + "phone": "Phone (optional)", + "phonePlaceholder": "+49 7531 12345" }, "edit": { "title": "Edit place", diff --git a/apps/citycorners/apps/web/src/routes/(app)/+page.svelte b/apps/citycorners/apps/web/src/routes/(app)/+page.svelte index 30b1dcff0..db37c3b2e 100644 --- a/apps/citycorners/apps/web/src/routes/(app)/+page.svelte +++ b/apps/citycorners/apps/web/src/routes/(app)/+page.svelte @@ -7,6 +7,7 @@ interface Location { id: string; + slug?: string; name: string; category: string; description: string; @@ -179,7 +180,7 @@ + +
+ + +
+ + +
+ + +
+
-{:else if loading} -

{$_('home.loading')}

-{:else if favoriteLocations.length === 0} -
- 💙 -

{$_('favorites.empty')}

-
{:else} -
- {#each favoriteLocations as location} - - {#if location.imageUrl} - {location.name} - {:else} -
- 📍 -
- {/if} -
- {$_(`category.${location.category}`)} -

- {location.name} -

-
- -
- {/each} + +
+ +
+ + {#if activeTab === 'favorites'} + {#if loading} +

{$_('home.loading')}

+ {:else if favoriteLocations.length === 0} +
+ 💙 +

{$_('favorites.empty')}

+
+ {:else} + + {/if} + {:else if selectedCollection} + +
+ +
+
+
+

{selectedCollection.name}

+ {#if selectedCollection.description} +

{selectedCollection.description}

+ {/if} +
+ +
+ {#if selectedCollectionLocations.length === 0} +
+

{$_('collections.noLocations')}

+
+ {:else} + + {/if} + {:else} + +
+ +
+ + {#if showCreateForm} +
+
+ + +
+
+ + +
+
+ + +
+
+ {/if} + + {#if collections.length === 0} +
+

{$_('collections.empty')}

+
+ {:else} +
+ {#each collections as collection} + + {/each} +
+ {/if} + {/if} {/if} diff --git a/apps/citycorners/apps/web/src/routes/(app)/locations/[id]/+page.svelte b/apps/citycorners/apps/web/src/routes/(app)/locations/[id]/+page.svelte index 7332e07f3..2d0501b56 100644 --- a/apps/citycorners/apps/web/src/routes/(app)/locations/[id]/+page.svelte +++ b/apps/citycorners/apps/web/src/routes/(app)/locations/[id]/+page.svelte @@ -21,6 +21,7 @@ interface NearbyLocation { id: string; + slug?: string; name: string; category: string; imageUrl?: string; @@ -29,6 +30,7 @@ interface Location { id: string; + slug?: string; name: string; category: string; description: string; @@ -38,6 +40,9 @@ imageUrl?: string; images?: LocationImage[]; timeline?: TimelineEntry[]; + website?: string; + phone?: string; + openingHours?: Record; createdBy?: string; } @@ -443,6 +448,58 @@

{location.description}

+ + {#if location.website || location.phone} +
+ {#if location.website} + + {/if} + {#if location.phone} +
+ {$_('detail.phone')}: + + {location.phone} + +
+ {/if} +
+ {/if} + + + {#if location.openingHours && Object.keys(location.openingHours).length > 0} +
+

{$_('detail.openingHours')}

+
+ + + {#each ['mo', 'tu', 'we', 'th', 'fr', 'sa', 'su'] as day} + {#if location.openingHours[day]} + + + + + {/if} + {/each} + +
{$_(`days.${day}`)} + {location.openingHours[day] === 'closed' + ? $_('detail.closed') + : location.openingHours[day]} +
+
+
+ {/if} + {#if isOwner}
@@ -613,7 +670,7 @@ + +
+ + +
+ + +
+ + +
+
= 10; + let markerLayer: any; + + if (useCluster) { + const { default: MCG } = await import('leaflet.markercluster'); + markerLayer = (L as any).markerClusterGroup(); + } + for (const loc of locations) { if (loc.latitude && loc.longitude) { const color = categoryColors[loc.category] || '#6b7280'; @@ -60,18 +69,28 @@ iconAnchor: [14, 14], }); - const marker = L.marker([loc.latitude, loc.longitude], { icon }).addTo(map); + const marker = L.marker([loc.latitude, loc.longitude], { icon }); marker.bindPopup(` `); + + if (useCluster && markerLayer) { + markerLayer.addLayer(marker); + } else { + marker.addTo(map); + } } } + + if (useCluster && markerLayer) { + map.addLayer(markerLayer); + } }); function handleLocateMe() { @@ -117,6 +136,16 @@ {$_('map.title')} - CityCorners + +
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 84f630362..4ea37c47e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -165,7 +165,7 @@ importers: devDependencies: '@nestjs/cli': specifier: ^10.4.9 - version: 10.4.9(esbuild@0.27.4) + version: 10.4.9(esbuild@0.19.12) '@nestjs/schematics': specifier: ^10.2.3 version: 10.2.3(chokidar@3.6.0)(typescript@5.9.3) @@ -204,7 +204,7 @@ importers: version: 5.5.4(@types/eslint@9.6.1)(eslint-config-prettier@9.1.2(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1))(prettier@3.6.2) jest: specifier: ^30.3.0 - version: 30.3.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.27.4))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)) + version: 30.3.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.19.12))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)) prettier: specifier: ^3.4.2 version: 3.6.2 @@ -213,10 +213,10 @@ importers: version: 0.5.21 ts-jest: specifier: ^29.4.5 - version: 29.4.5(@babel/core@7.28.5)(@jest/transform@30.3.0)(@jest/types@30.3.0)(babel-jest@30.3.0(@babel/core@7.28.5))(esbuild@0.27.4)(jest-util@30.3.0)(jest@30.3.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.27.4))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))(typescript@5.9.3) + version: 29.4.5(@babel/core@7.28.5)(@jest/transform@30.3.0)(@jest/types@30.3.0)(babel-jest@30.3.0(@babel/core@7.28.5))(esbuild@0.19.12)(jest-util@30.3.0)(jest@30.3.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.19.12))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))(typescript@5.9.3) ts-loader: specifier: ^9.5.1 - version: 9.5.4(typescript@5.9.3)(webpack@5.100.2(esbuild@0.27.4)) + version: 9.5.4(typescript@5.9.3)(webpack@5.97.1(esbuild@0.19.12)) ts-node: specifier: ^10.9.2 version: 10.9.2(@types/node@22.19.1)(typescript@5.9.3) @@ -240,14 +240,14 @@ importers: version: link:../../../../packages/shared-landing-ui astro: specifier: ^5.16.0 - version: 5.16.0(@netlify/blobs@10.4.1)(@types/node@20.19.25)(ioredis@5.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.53.3)(terser@5.44.1)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.1) + version: 5.16.0(@netlify/blobs@10.4.1)(@types/node@20.19.25)(ioredis@5.9.2)(jiti@1.21.7)(lightningcss@1.30.2)(rollup@4.53.3)(terser@5.44.1)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.1) typescript: specifier: ^5.9.2 version: 5.9.3 devDependencies: '@astrojs/tailwind': specifier: ^6.0.2 - version: 6.0.2(astro@5.16.0(@netlify/blobs@10.4.1)(@types/node@20.19.25)(ioredis@5.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.53.3)(terser@5.44.1)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.1))(tailwindcss@3.4.18(tsx@4.21.0)(yaml@2.8.1))(ts-node@10.9.2(@types/node@20.19.25)(typescript@5.9.3)) + version: 6.0.2(astro@5.16.0(@netlify/blobs@10.4.1)(@types/node@20.19.25)(ioredis@5.9.2)(jiti@1.21.7)(lightningcss@1.30.2)(rollup@4.53.3)(terser@5.44.1)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.1))(tailwindcss@3.4.18(tsx@4.21.0)(yaml@2.8.1))(ts-node@10.9.2(@types/node@20.19.25)(typescript@5.9.3)) '@tailwindcss/typography': specifier: ^0.5.18 version: 0.5.19(tailwindcss@3.4.18(tsx@4.21.0)(yaml@2.8.1)) @@ -256,13 +256,13 @@ importers: version: 20.19.25 eslint: specifier: ^9.0.0 - version: 9.39.1(jiti@2.6.1) + version: 9.39.1(jiti@1.21.7) eslint-config-prettier: specifier: ^9.1.0 - version: 9.1.2(eslint@9.39.1(jiti@2.6.1)) + version: 9.1.2(eslint@9.39.1(jiti@1.21.7)) eslint-plugin-astro: specifier: ^1.0.0 - version: 1.5.0(eslint@9.39.1(jiti@2.6.1)) + version: 1.5.0(eslint@9.39.1(jiti@1.21.7)) prettier: specifier: ^3.6.2 version: 3.6.2 @@ -691,19 +691,19 @@ importers: version: 18.3.27 '@typescript-eslint/eslint-plugin': specifier: ^7.7.0 - version: 7.18.0(@typescript-eslint/parser@7.18.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3))(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3) + version: 7.18.0(@typescript-eslint/parser@7.18.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3) '@typescript-eslint/parser': specifier: ^7.7.0 - version: 7.18.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3) + version: 7.18.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3) dotenv: specifier: ^16.4.7 version: 16.6.1 eslint: specifier: ^9.39.1 - version: 9.39.1(jiti@1.21.7) + version: 9.39.1(jiti@2.6.1) eslint-config-universe: specifier: ^12.0.1 - version: 12.1.0(@types/eslint@9.6.1)(eslint@9.39.1(jiti@1.21.7))(prettier@3.6.2)(typescript@5.3.3) + version: 12.1.0(@types/eslint@9.6.1)(eslint@9.39.1(jiti@2.6.1))(prettier@3.6.2)(typescript@5.3.3) prettier: specifier: ^3.2.5 version: 3.6.2 @@ -987,6 +987,9 @@ importers: leaflet: specifier: ^1.9.4 version: 1.9.4 + leaflet.markercluster: + specifier: ^1.5.3 + version: 1.5.3(leaflet@1.9.4) svelte-i18n: specifier: ^4.0.1 version: 4.0.1(svelte@5.44.0) @@ -1012,6 +1015,9 @@ importers: '@types/leaflet': specifier: ^1.9.8 version: 1.9.21 + '@types/leaflet.markercluster': + specifier: ^1.5.6 + version: 1.5.6 '@types/node': specifier: ^20.0.0 version: 20.19.25 @@ -1036,6 +1042,9 @@ importers: vite: specifier: ^6.0.0 version: 6.4.1(@types/node@20.19.25)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.1) + vitest: + specifier: ^3.2.1 + version: 3.2.4(@types/debug@4.1.12)(@types/node@20.19.25)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(jiti@2.6.1)(jsdom@27.2.0)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.1) apps/clock/apps/backend: dependencies: @@ -2064,7 +2073,7 @@ importers: dependencies: '@expo/vector-icons': specifier: ^15.0.3 - version: 15.0.3(expo-font@55.0.4(expo@54.0.25)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) + version: 15.0.3(expo-font@14.0.10(expo@54.0.25)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) '@manacore/shared-auth': specifier: workspace:* version: link:../../../../packages/shared-auth @@ -2433,7 +2442,7 @@ importers: version: 7.1.4 ts-jest: specifier: ^29.2.5 - version: 29.4.5(@babel/core@7.28.5)(@jest/transform@30.3.0)(@jest/types@30.3.0)(babel-jest@30.3.0(@babel/core@7.28.5))(esbuild@0.27.4)(jest-util@30.3.0)(jest@30.2.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.27.4))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))(typescript@5.9.3) + version: 29.4.5(@babel/core@7.28.5)(@jest/transform@30.3.0)(@jest/types@30.3.0)(babel-jest@30.3.0(@babel/core@7.28.5))(esbuild@0.27.4)(jest-util@30.3.0)(jest@30.2.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.27.4)))(typescript@5.9.3) ts-loader: specifier: ^9.5.2 version: 9.5.4(typescript@5.9.3)(webpack@5.100.2(esbuild@0.27.4)) @@ -5202,7 +5211,7 @@ importers: version: 30.2.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.27.4))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)) ts-jest: specifier: ^29.2.5 - version: 29.4.5(@babel/core@7.28.5)(@jest/transform@30.3.0)(@jest/types@30.3.0)(babel-jest@30.3.0(@babel/core@7.28.5))(esbuild@0.27.4)(jest-util@30.3.0)(jest@30.2.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.27.4))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))(typescript@5.9.3) + version: 29.4.5(@babel/core@7.28.5)(@jest/transform@30.3.0)(@jest/types@30.3.0)(babel-jest@30.3.0(@babel/core@7.28.5))(esbuild@0.27.4)(jest-util@30.3.0)(jest@30.2.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.27.4)))(typescript@5.9.3) tsx: specifier: ^4.19.4 version: 4.20.6 @@ -5664,7 +5673,7 @@ importers: version: 30.2.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.27.4))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)) ts-jest: specifier: ^29.2.5 - version: 29.4.5(@babel/core@7.28.5)(@jest/transform@30.3.0)(@jest/types@30.3.0)(babel-jest@30.3.0(@babel/core@7.28.5))(esbuild@0.27.4)(jest-util@30.3.0)(jest@30.2.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.27.4))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))(typescript@5.9.3) + version: 29.4.5(@babel/core@7.28.5)(@jest/transform@30.3.0)(@jest/types@30.3.0)(babel-jest@30.3.0(@babel/core@7.28.5))(esbuild@0.27.4)(jest-util@30.3.0)(jest@30.2.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.27.4)))(typescript@5.9.3) tsx: specifier: ^4.19.4 version: 4.20.6 @@ -7261,7 +7270,7 @@ importers: version: 1.57.0 jest: specifier: ^29.0.0 - version: 29.7.0(@types/node@24.10.1)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)) + version: 29.7.0(@types/node@24.10.1) vitest: specifier: ^3.0.0 version: 3.2.4(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(jiti@2.6.1)(jsdom@27.2.0)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.1) @@ -8546,6 +8555,9 @@ importers: '@manacore/matrix-bot-common': specifier: workspace:* version: link:../../packages/matrix-bot-common + '@manacore/shared-llm': + specifier: workspace:^ + version: link:../../packages/shared-llm '@nestjs/common': specifier: ^10.4.15 version: 10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2) @@ -8576,7 +8588,7 @@ importers: devDependencies: '@nestjs/cli': specifier: ^10.4.9 - version: 10.4.9(esbuild@0.19.12) + version: 10.4.9(esbuild@0.27.4) '@nestjs/schematics': specifier: ^10.2.3 version: 10.2.3(chokidar@3.6.0)(typescript@5.9.3) @@ -15528,6 +15540,9 @@ packages: '@types/jsonwebtoken@9.0.10': resolution: {integrity: sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==} + '@types/leaflet.markercluster@1.5.6': + resolution: {integrity: sha512-I7hZjO2+isVXGYWzKxBp8PsCzAYCJBc29qBdFpquOCkS7zFDqUsUvkEOyQHedsk/Cy5tocQzf+Ndorm5W9YKTQ==} + '@types/leaflet@1.9.21': resolution: {integrity: sha512-TbAd9DaPGSnzp6QvtYngntMZgcRk+igFELwR2N99XZn7RXUdKgsXMR+28bUO0rPsWp8MIu/f47luLIQuSLYv/w==} @@ -21849,6 +21864,11 @@ packages: leac@0.6.0: resolution: {integrity: sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg==} + leaflet.markercluster@1.5.3: + resolution: {integrity: sha512-vPTw/Bndq7eQHjLBVlWpnGeLa3t+3zGiuM7fJwCkiMFq+nmRuG3RI3f7f4N4TDX7T4NpbAXpR2+NTRSEGfCSeA==} + peerDependencies: + leaflet: ^1.3.1 + leaflet@1.9.4: resolution: {integrity: sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==} @@ -27855,6 +27875,16 @@ snapshots: transitivePeerDependencies: - ts-node + '@astrojs/tailwind@6.0.2(astro@5.16.0(@netlify/blobs@10.4.1)(@types/node@20.19.25)(ioredis@5.9.2)(jiti@1.21.7)(lightningcss@1.30.2)(rollup@4.53.3)(terser@5.44.1)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.1))(tailwindcss@3.4.18(tsx@4.21.0)(yaml@2.8.1))(ts-node@10.9.2(@types/node@20.19.25)(typescript@5.9.3))': + dependencies: + astro: 5.16.0(@netlify/blobs@10.4.1)(@types/node@20.19.25)(ioredis@5.9.2)(jiti@1.21.7)(lightningcss@1.30.2)(rollup@4.53.3)(terser@5.44.1)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.1) + autoprefixer: 10.4.22(postcss@8.5.6) + postcss: 8.5.6 + postcss-load-config: 4.0.2(postcss@8.5.6)(ts-node@10.9.2(@types/node@20.19.25)(typescript@5.9.3)) + tailwindcss: 3.4.18(tsx@4.21.0)(yaml@2.8.1) + transitivePeerDependencies: + - ts-node + '@astrojs/tailwind@6.0.2(astro@5.16.0(@netlify/blobs@10.4.1)(@types/node@20.19.25)(ioredis@5.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.53.3)(terser@5.44.1)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.1))(tailwindcss@3.4.18(tsx@4.21.0)(yaml@2.8.1))(ts-node@10.9.2(@types/node@20.19.25)(typescript@5.9.3))': dependencies: astro: 5.16.0(@netlify/blobs@10.4.1)(@types/node@20.19.25)(ioredis@5.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.53.3)(terser@5.44.1)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.1) @@ -30833,7 +30863,7 @@ snapshots: wrap-ansi: 7.0.0 ws: 8.18.3 optionalDependencies: - expo-router: 55.0.5(qwxmdxiornnsbyvrtivw4g2joq) + expo-router: 55.0.5(xobbmdsfsaswzvlwz73gklusxm) react-native: 0.81.4(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.1.0) transitivePeerDependencies: - '@modelcontextprotocol/sdk' @@ -31210,7 +31240,7 @@ snapshots: ws: 8.18.3 zod: 3.25.76 optionalDependencies: - expo-router: 55.0.5(tkph4mqwn7yyg5tlp6kukooce4) + expo-router: 55.0.5(apnkrhypuo4jtg23v6qzhb7sxe) react-native: 0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4) transitivePeerDependencies: - '@expo/dom-webview' @@ -31904,7 +31934,7 @@ snapshots: postcss: 8.4.49 resolve-from: 5.0.0 optionalDependencies: - expo: 54.0.25(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@6.1.2)(expo-router@55.0.5)(react-native-webview@13.12.2(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.1.0))(react@19.1.0) + expo: 54.0.25(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@6.1.2)(expo-router@6.0.15)(react-native-webview@13.12.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) transitivePeerDependencies: - bufferutil - supports-color @@ -32223,7 +32253,7 @@ snapshots: '@expo/json-file': 10.0.7 '@react-native/normalize-colors': 0.81.5 debug: 4.4.3 - expo: 54.0.25(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@6.1.2)(expo-router@55.0.5)(react-native-webview@13.12.2(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.1.0))(react@19.1.0) + expo: 54.0.25(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@6.1.2)(expo-router@6.0.15)(react-native-webview@13.12.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) resolve-from: 5.0.0 semver: 7.7.3 xml2js: 0.6.0 @@ -32309,7 +32339,7 @@ snapshots: react: 19.2.4 optionalDependencies: '@expo/metro-runtime': 6.1.2(expo@55.0.5)(react-dom@19.2.4(react@19.2.4))(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) - expo-router: 55.0.5(tkph4mqwn7yyg5tlp6kukooce4) + expo-router: 55.0.5(apnkrhypuo4jtg23v6qzhb7sxe) react-dom: 19.2.4(react@19.2.4) transitivePeerDependencies: - supports-color @@ -32388,6 +32418,12 @@ snapshots: react: 19.1.0 react-native: 0.81.4(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0) + '@expo/vector-icons@15.0.3(expo-font@14.0.10(expo@54.0.25)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.1.0))(react@19.1.0)': + dependencies: + expo-font: 14.0.10(expo@54.0.25)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.1.0))(react@19.1.0) + react: 19.1.0 + react-native: 0.81.4(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.1.0) + '@expo/vector-icons@15.0.3(expo-font@14.0.10(expo@54.0.25)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1)': dependencies: expo-font: 14.0.10(expo@54.0.25)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1) @@ -32400,12 +32436,6 @@ snapshots: react: 19.1.0 react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0) - '@expo/vector-icons@15.0.3(expo-font@14.0.10)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.1.0))(react@19.1.0)': - dependencies: - expo-font: 14.0.10(expo@54.0.25)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.1.0))(react@19.1.0) - react: 19.1.0 - react-native: 0.81.4(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.1.0) - '@expo/vector-icons@15.0.3(expo-font@14.0.9(expo@54.0.13)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0)': dependencies: expo-font: 14.0.9(expo@54.0.13)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) @@ -32418,12 +32448,6 @@ snapshots: react: 19.1.0 react-native: 0.81.4(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0) - '@expo/vector-icons@15.0.3(expo-font@55.0.4(expo@54.0.25)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0)': - dependencies: - expo-font: 55.0.4(expo@54.0.25)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) - react: 19.1.0 - react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0) - '@expo/vector-icons@15.0.3(expo-font@55.0.4)(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)': dependencies: expo-font: 55.0.4(expo@55.0.5)(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0) @@ -33026,41 +33050,6 @@ snapshots: - supports-color - ts-node - '@jest/core@29.7.0(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3))': - dependencies: - '@jest/console': 29.7.0 - '@jest/reporters': 29.7.0 - '@jest/test-result': 29.7.0 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 22.19.1 - ansi-escapes: 4.3.2 - chalk: 4.1.2 - ci-info: 3.9.0 - exit: 0.1.2 - graceful-fs: 4.2.11 - jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)) - jest-haste-map: 29.7.0 - jest-message-util: 29.7.0 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-resolve-dependencies: 29.7.0 - jest-runner: 29.7.0 - jest-runtime: 29.7.0 - jest-snapshot: 29.7.0 - jest-util: 29.7.0 - jest-validate: 29.7.0 - jest-watcher: 29.7.0 - micromatch: 4.0.8 - pretty-format: 29.7.0 - slash: 3.0.0 - strip-ansi: 6.0.1 - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - - ts-node - '@jest/core@30.2.0(esbuild-register@3.6.0(esbuild@0.27.4))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))': dependencies: '@jest/console': 30.2.0 @@ -33097,7 +33086,7 @@ snapshots: - supports-color - ts-node - '@jest/core@30.3.0(esbuild-register@3.6.0(esbuild@0.19.12))': + '@jest/core@30.3.0(esbuild-register@3.6.0(esbuild@0.19.12))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))': dependencies: '@jest/console': 30.3.0 '@jest/pattern': 30.0.1 @@ -33112,43 +33101,7 @@ snapshots: exit-x: 0.2.2 graceful-fs: 4.2.11 jest-changed-files: 30.3.0 - jest-config: 30.3.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.19.12)) - jest-haste-map: 30.3.0 - jest-message-util: 30.3.0 - jest-regex-util: 30.0.1 - jest-resolve: 30.3.0 - jest-resolve-dependencies: 30.3.0 - jest-runner: 30.3.0 - jest-runtime: 30.3.0 - jest-snapshot: 30.3.0 - jest-util: 30.3.0 - jest-validate: 30.3.0 - jest-watcher: 30.3.0 - pretty-format: 30.3.0 - slash: 3.0.0 - transitivePeerDependencies: - - babel-plugin-macros - - esbuild-register - - supports-color - - ts-node - optional: true - - '@jest/core@30.3.0(esbuild-register@3.6.0(esbuild@0.27.4))': - dependencies: - '@jest/console': 30.3.0 - '@jest/pattern': 30.0.1 - '@jest/reporters': 30.3.0 - '@jest/test-result': 30.3.0 - '@jest/transform': 30.3.0 - '@jest/types': 30.3.0 - '@types/node': 22.19.1 - ansi-escapes: 4.3.2 - chalk: 4.1.2 - ci-info: 4.3.1 - exit-x: 0.2.2 - graceful-fs: 4.2.11 - jest-changed-files: 30.3.0 - jest-config: 30.3.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.27.4)) + jest-config: 30.3.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.19.12))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)) jest-haste-map: 30.3.0 jest-message-util: 30.3.0 jest-regex-util: 30.0.1 @@ -39263,7 +39216,7 @@ snapshots: jest: 30.3.0(@types/node@20.19.25)(esbuild-register@3.6.0(esbuild@0.27.4)) optional: true - '@testing-library/react-native@13.3.3(jest@30.3.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.19.12)))(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react-test-renderer@19.1.0(react@19.2.4))(react@19.2.4)': + '@testing-library/react-native@13.3.3(jest@30.3.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.19.12))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react-test-renderer@19.1.0(react@19.2.4))(react@19.2.4)': dependencies: jest-matcher-utils: 30.3.0 picocolors: 1.1.1 @@ -39273,7 +39226,7 @@ snapshots: react-test-renderer: 19.1.0(react@19.2.4) redent: 3.0.0 optionalDependencies: - jest: 30.3.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.19.12)) + jest: 30.3.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.19.12))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)) optional: true '@testing-library/react-native@13.3.3(jest@30.3.0(esbuild-register@3.6.0(esbuild@0.27.4)))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react-test-renderer@19.1.0(react@19.1.0))(react@19.1.0)': @@ -39286,7 +39239,7 @@ snapshots: react-test-renderer: 19.1.0(react@19.1.0) redent: 3.0.0 optionalDependencies: - jest: 30.3.0(esbuild-register@3.6.0(esbuild@0.27.4)) + jest: 30.3.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.27.4)) optional: true '@testing-library/react-native@13.3.3(jest@30.3.0(esbuild-register@3.6.0(esbuild@0.27.4)))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react-test-renderer@19.1.0(react@19.1.0))(react@19.1.0)': @@ -39299,7 +39252,7 @@ snapshots: react-test-renderer: 19.1.0(react@19.1.0) redent: 3.0.0 optionalDependencies: - jest: 30.3.0(esbuild-register@3.6.0(esbuild@0.27.4)) + jest: 30.3.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.27.4)) optional: true '@testing-library/react-native@13.3.3(jest@30.3.0(esbuild-register@3.6.0(esbuild@0.27.4)))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react-test-renderer@19.1.0(react@18.3.1))(react@18.3.1)': @@ -39312,7 +39265,7 @@ snapshots: react-test-renderer: 19.1.0(react@18.3.1) redent: 3.0.0 optionalDependencies: - jest: 30.3.0(esbuild-register@3.6.0(esbuild@0.27.4)) + jest: 30.3.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.27.4)) optional: true '@testing-library/react-native@13.3.3(jest@30.3.0(esbuild-register@3.6.0(esbuild@0.27.4)))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react-test-renderer@19.1.0(react@19.1.0))(react@19.1.0)': @@ -39325,7 +39278,7 @@ snapshots: react-test-renderer: 19.1.0(react@19.1.0) redent: 3.0.0 optionalDependencies: - jest: 30.3.0(esbuild-register@3.6.0(esbuild@0.27.4)) + jest: 30.3.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.27.4)) optional: true '@testing-library/react-native@13.3.3(jest@30.3.0(esbuild-register@3.6.0(esbuild@0.27.4)))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.0))(react-test-renderer@19.1.0(react@19.2.0))(react@19.2.0)': @@ -39338,7 +39291,7 @@ snapshots: react-test-renderer: 19.1.0(react@19.2.0) redent: 3.0.0 optionalDependencies: - jest: 30.3.0(esbuild-register@3.6.0(esbuild@0.27.4)) + jest: 30.3.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.27.4)) optional: true '@testing-library/svelte-core@1.0.0(svelte@5.44.0)': @@ -39722,6 +39675,10 @@ snapshots: '@types/ms': 2.1.0 '@types/node': 22.19.1 + '@types/leaflet.markercluster@1.5.6': + dependencies: + '@types/leaflet': 1.9.21 + '@types/leaflet@1.9.21': dependencies: '@types/geojson': 7946.0.16 @@ -39981,16 +39938,16 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3))(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3)': + '@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 6.21.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3) + '@typescript-eslint/parser': 6.21.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3) '@typescript-eslint/scope-manager': 6.21.0 - '@typescript-eslint/type-utils': 6.21.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3) - '@typescript-eslint/utils': 6.21.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3) + '@typescript-eslint/type-utils': 6.21.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3) + '@typescript-eslint/utils': 6.21.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3) '@typescript-eslint/visitor-keys': 6.21.0 debug: 4.4.3 - eslint: 9.39.1(jiti@1.21.7) + eslint: 9.39.1(jiti@2.6.1) graphemer: 1.4.0 ignore: 5.3.2 natural-compare: 1.4.0 @@ -40039,15 +39996,15 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@7.18.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3))(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3)': + '@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@7.18.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 7.18.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3) + '@typescript-eslint/parser': 7.18.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3) '@typescript-eslint/scope-manager': 7.18.0 - '@typescript-eslint/type-utils': 7.18.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3) - '@typescript-eslint/utils': 7.18.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3) + '@typescript-eslint/type-utils': 7.18.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3) + '@typescript-eslint/utils': 7.18.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3) '@typescript-eslint/visitor-keys': 7.18.0 - eslint: 9.39.1(jiti@1.21.7) + eslint: 9.39.1(jiti@2.6.1) graphemer: 1.4.0 ignore: 5.3.2 natural-compare: 1.4.0 @@ -40139,14 +40096,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@6.21.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3)': + '@typescript-eslint/parser@6.21.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3)': dependencies: '@typescript-eslint/scope-manager': 6.21.0 '@typescript-eslint/types': 6.21.0 '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.3.3) '@typescript-eslint/visitor-keys': 6.21.0 debug: 4.4.3 - eslint: 9.39.1(jiti@1.21.7) + eslint: 9.39.1(jiti@2.6.1) optionalDependencies: typescript: 5.3.3 transitivePeerDependencies: @@ -40178,14 +40135,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@7.18.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3)': + '@typescript-eslint/parser@7.18.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3)': dependencies: '@typescript-eslint/scope-manager': 7.18.0 '@typescript-eslint/types': 7.18.0 '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.3.3) '@typescript-eslint/visitor-keys': 7.18.0 debug: 4.4.3 - eslint: 9.39.1(jiti@1.21.7) + eslint: 9.39.1(jiti@2.6.1) optionalDependencies: typescript: 5.3.3 transitivePeerDependencies: @@ -40311,12 +40268,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/type-utils@6.21.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3)': + '@typescript-eslint/type-utils@6.21.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3)': dependencies: '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.3.3) - '@typescript-eslint/utils': 6.21.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3) + '@typescript-eslint/utils': 6.21.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3) debug: 4.4.3 - eslint: 9.39.1(jiti@1.21.7) + eslint: 9.39.1(jiti@2.6.1) ts-api-utils: 1.4.3(typescript@5.3.3) optionalDependencies: typescript: 5.3.3 @@ -40347,12 +40304,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/type-utils@7.18.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3)': + '@typescript-eslint/type-utils@7.18.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3)': dependencies: '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.3.3) - '@typescript-eslint/utils': 7.18.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3) + '@typescript-eslint/utils': 7.18.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3) debug: 4.4.3 - eslint: 9.39.1(jiti@1.21.7) + eslint: 9.39.1(jiti@2.6.1) ts-api-utils: 1.4.3(typescript@5.3.3) optionalDependencies: typescript: 5.3.3 @@ -40534,15 +40491,15 @@ snapshots: - supports-color - typescript - '@typescript-eslint/utils@6.21.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3)': + '@typescript-eslint/utils@6.21.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3)': dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@1.21.7)) + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@2.6.1)) '@types/json-schema': 7.0.15 '@types/semver': 7.7.1 '@typescript-eslint/scope-manager': 6.21.0 '@typescript-eslint/types': 6.21.0 '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.3.3) - eslint: 9.39.1(jiti@1.21.7) + eslint: 9.39.1(jiti@2.6.1) semver: 7.7.3 transitivePeerDependencies: - supports-color @@ -40573,13 +40530,13 @@ snapshots: - supports-color - typescript - '@typescript-eslint/utils@7.18.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3)': + '@typescript-eslint/utils@7.18.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3)': dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@1.21.7)) + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@2.6.1)) '@typescript-eslint/scope-manager': 7.18.0 '@typescript-eslint/types': 7.18.0 '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.3.3) - eslint: 9.39.1(jiti@1.21.7) + eslint: 9.39.1(jiti@2.6.1) transitivePeerDependencies: - supports-color - typescript @@ -40797,7 +40754,7 @@ snapshots: magic-string: 0.30.21 sirv: 3.0.2 tinyrainbow: 2.0.0 - vitest: 3.2.4(@types/debug@4.1.12)(@types/node@20.19.25)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(jiti@2.6.1)(jsdom@26.1.0)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.1) + vitest: 3.2.4(@types/debug@4.1.12)(@types/node@20.19.25)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(jiti@2.6.1)(jsdom@27.2.0)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.1) ws: 8.18.3 optionalDependencies: playwright: 1.57.0 @@ -40808,11 +40765,11 @@ snapshots: - vite optional: true - '@vitest/browser@3.2.4(playwright@1.57.0)(vite@6.4.1(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.1))(vitest@3.2.4)': + '@vitest/browser@3.2.4(playwright@1.57.0)(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.1))(vitest@3.2.4)': dependencies: '@testing-library/dom': 10.4.1 '@testing-library/user-event': 14.6.1(@testing-library/dom@10.4.1) - '@vitest/mocker': 3.2.4(vite@6.4.1(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.1)) + '@vitest/mocker': 3.2.4(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.1)) '@vitest/utils': 3.2.4 magic-string: 0.30.21 sirv: 3.0.2 @@ -40881,7 +40838,7 @@ snapshots: '@types/chai': 5.2.3 '@vitest/spy': 4.0.18 '@vitest/utils': 4.0.18 - chai: 6.2.1 + chai: 6.2.2 tinyrainbow: 3.0.3 '@vitest/expect@4.1.0': @@ -40925,6 +40882,15 @@ snapshots: optionalDependencies: vite: 6.4.1(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.1) + '@vitest/mocker@3.2.4(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.1))': + dependencies: + '@vitest/spy': 3.2.4 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 7.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.1) + optional: true + '@vitest/mocker@4.0.14(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.1))': dependencies: '@vitest/spy': 4.0.14 @@ -41081,7 +41047,7 @@ snapshots: sirv: 3.0.2 tinyglobby: 0.2.15 tinyrainbow: 2.0.0 - vitest: 3.2.4(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(jiti@2.6.1)(jsdom@27.2.0)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.1) + vitest: 3.2.4(@types/debug@4.1.12)(@types/node@20.19.25)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(jiti@2.6.1)(jsdom@27.2.0)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.1) optional: true '@vitest/ui@4.0.14(vitest@4.0.14)': @@ -41640,6 +41606,108 @@ snapshots: transitivePeerDependencies: - supports-color + astro@5.16.0(@netlify/blobs@10.4.1)(@types/node@20.19.25)(ioredis@5.9.2)(jiti@1.21.7)(lightningcss@1.30.2)(rollup@4.53.3)(terser@5.44.1)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.1): + dependencies: + '@astrojs/compiler': 2.13.0 + '@astrojs/internal-helpers': 0.7.5 + '@astrojs/markdown-remark': 6.3.9 + '@astrojs/telemetry': 3.3.0 + '@capsizecss/unpack': 3.0.1 + '@oslojs/encoding': 1.1.0 + '@rollup/pluginutils': 5.3.0(rollup@4.53.3) + acorn: 8.15.0 + aria-query: 5.3.2 + axobject-query: 4.1.0 + boxen: 8.0.1 + ci-info: 4.3.1 + clsx: 2.1.1 + common-ancestor-path: 1.0.1 + cookie: 1.1.0 + cssesc: 3.0.0 + debug: 4.4.3 + deterministic-object-hash: 2.0.2 + devalue: 5.5.0 + diff: 5.2.0 + dlv: 1.1.3 + dset: 3.1.4 + es-module-lexer: 1.7.0 + esbuild: 0.25.12 + estree-walker: 3.0.3 + flattie: 1.1.1 + fontace: 0.3.1 + github-slugger: 2.0.0 + html-escaper: 3.0.3 + http-cache-semantics: 4.2.0 + import-meta-resolve: 4.2.0 + js-yaml: 4.1.1 + magic-string: 0.30.21 + magicast: 0.5.1 + mrmime: 2.0.1 + neotraverse: 0.6.18 + p-limit: 6.2.0 + p-queue: 8.1.1 + package-manager-detector: 1.5.0 + piccolore: 0.1.3 + picomatch: 4.0.3 + prompts: 2.4.2 + rehype: 13.0.2 + semver: 7.7.3 + shiki: 3.15.0 + smol-toml: 1.5.2 + svgo: 4.0.0 + tinyexec: 1.0.2 + tinyglobby: 0.2.15 + tsconfck: 3.1.6(typescript@5.9.3) + ultrahtml: 1.6.0 + unifont: 0.6.0 + unist-util-visit: 5.0.0 + unstorage: 1.17.3(@netlify/blobs@10.4.1)(ioredis@5.9.2) + vfile: 6.0.3 + vite: 6.4.1(@types/node@20.19.25)(jiti@1.21.7)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.1) + vitefu: 1.1.1(vite@6.4.1(@types/node@20.19.25)(jiti@1.21.7)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.1)) + xxhash-wasm: 1.1.0 + yargs-parser: 21.1.1 + yocto-spinner: 0.2.3 + zod: 3.25.76 + zod-to-json-schema: 3.25.0(zod@3.25.76) + zod-to-ts: 1.2.0(typescript@5.9.3)(zod@3.25.76) + optionalDependencies: + sharp: 0.34.5 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@types/node' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - db0 + - idb-keyval + - ioredis + - jiti + - less + - lightningcss + - rollup + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - typescript + - uploadthing + - yaml + astro@5.16.0(@netlify/blobs@10.4.1)(@types/node@20.19.25)(ioredis@5.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.53.3)(terser@5.44.1)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.1): dependencies: '@astrojs/compiler': 2.13.0 @@ -42266,7 +42334,7 @@ snapshots: resolve-from: 5.0.0 optionalDependencies: '@babel/runtime': 7.28.4 - expo: 54.0.25(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@6.1.2)(expo-router@55.0.5)(react-native-webview@13.12.2(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.1.0))(react@19.1.0) + expo: 54.0.25(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@6.1.2)(expo-router@6.0.15)(react-native-webview@13.12.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) transitivePeerDependencies: - '@babel/core' - supports-color @@ -43155,13 +43223,13 @@ snapshots: - supports-color - ts-node - create-jest@29.7.0(@types/node@24.10.1)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)): + create-jest@29.7.0(@types/node@24.10.1): dependencies: '@jest/types': 29.6.3 chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@24.10.1)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)) + jest-config: 29.7.0(@types/node@24.10.1) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -44327,6 +44395,11 @@ snapshots: eslint: 9.39.1(jiti@2.6.1) semver: 7.7.3 + eslint-compat-utils@0.6.5(eslint@9.39.1(jiti@1.21.7)): + dependencies: + eslint: 9.39.1(jiti@1.21.7) + semver: 7.7.3 + eslint-compat-utils@0.6.5(eslint@9.39.1(jiti@2.6.1)): dependencies: eslint: 9.39.1(jiti@2.6.1) @@ -44339,7 +44412,7 @@ snapshots: eslint: 9.39.1(jiti@2.6.1) eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@2.6.1)) eslint-plugin-expo: 1.0.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@2.6.1)) eslint-plugin-react: 7.37.5(eslint@9.39.1(jiti@2.6.1)) eslint-plugin-react-hooks: 5.2.0(eslint@9.39.1(jiti@2.6.1)) globals: 16.5.0 @@ -44356,7 +44429,7 @@ snapshots: eslint: 9.39.1(jiti@2.6.1) eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@2.6.1)) eslint-plugin-expo: 1.0.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@2.6.1)) eslint-plugin-react: 7.37.5(eslint@9.39.1(jiti@2.6.1)) eslint-plugin-react-hooks: 5.2.0(eslint@9.39.1(jiti@2.6.1)) globals: 16.5.0 @@ -44373,7 +44446,7 @@ snapshots: eslint: 9.39.1(jiti@2.6.1) eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@2.6.1)) eslint-plugin-expo: 0.1.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.8.3) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.1(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@2.6.1)) eslint-plugin-react: 7.37.5(eslint@9.39.1(jiti@2.6.1)) eslint-plugin-react-hooks: 5.2.0(eslint@9.39.1(jiti@2.6.1)) globals: 16.5.0 @@ -44391,14 +44464,14 @@ snapshots: dependencies: eslint: 8.57.1 - eslint-config-prettier@8.10.2(eslint@9.39.1(jiti@1.21.7)): - dependencies: - eslint: 9.39.1(jiti@1.21.7) - eslint-config-prettier@8.10.2(eslint@9.39.1(jiti@2.6.1)): dependencies: eslint: 9.39.1(jiti@2.6.1) + eslint-config-prettier@9.1.2(eslint@9.39.1(jiti@1.21.7)): + dependencies: + eslint: 9.39.1(jiti@1.21.7) + eslint-config-prettier@9.1.2(eslint@9.39.1(jiti@2.6.1)): dependencies: eslint: 9.39.1(jiti@2.6.1) @@ -44423,17 +44496,17 @@ snapshots: - supports-color - typescript - eslint-config-universe@12.1.0(@types/eslint@9.6.1)(eslint@9.39.1(jiti@1.21.7))(prettier@3.6.2)(typescript@5.3.3): + eslint-config-universe@12.1.0(@types/eslint@9.6.1)(eslint@9.39.1(jiti@2.6.1))(prettier@3.6.2)(typescript@5.3.3): dependencies: - '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3))(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3) - '@typescript-eslint/parser': 6.21.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3) - eslint: 9.39.1(jiti@1.21.7) - eslint-config-prettier: 8.10.2(eslint@9.39.1(jiti@1.21.7)) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@6.21.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3))(eslint@9.39.1(jiti@1.21.7)) - eslint-plugin-node: 11.1.0(eslint@9.39.1(jiti@1.21.7)) - eslint-plugin-prettier: 5.5.4(@types/eslint@9.6.1)(eslint-config-prettier@8.10.2(eslint@9.39.1(jiti@1.21.7)))(eslint@9.39.1(jiti@1.21.7))(prettier@3.6.2) - eslint-plugin-react: 7.37.5(eslint@9.39.1(jiti@1.21.7)) - eslint-plugin-react-hooks: 4.6.2(eslint@9.39.1(jiti@1.21.7)) + '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3) + '@typescript-eslint/parser': 6.21.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3) + eslint: 9.39.1(jiti@2.6.1) + eslint-config-prettier: 8.10.2(eslint@9.39.1(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@6.21.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3))(eslint@9.39.1(jiti@2.6.1)) + eslint-plugin-node: 11.1.0(eslint@9.39.1(jiti@2.6.1)) + eslint-plugin-prettier: 5.5.4(@types/eslint@9.6.1)(eslint-config-prettier@8.10.2(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1))(prettier@3.6.2) + eslint-plugin-react: 7.37.5(eslint@9.39.1(jiti@2.6.1)) + eslint-plugin-react-hooks: 4.6.2(eslint@9.39.1(jiti@2.6.1)) optionalDependencies: prettier: 3.6.2 transitivePeerDependencies: @@ -44469,7 +44542,7 @@ snapshots: '@typescript-eslint/parser': 8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) eslint: 9.39.1(jiti@2.6.1) eslint-config-prettier: 9.1.2(eslint@9.39.1(jiti@2.6.1)) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@2.6.1)) eslint-plugin-n: 17.24.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) eslint-plugin-node: 11.1.0(eslint@9.39.1(jiti@2.6.1)) eslint-plugin-prettier: 5.5.4(@types/eslint@9.6.1)(eslint-config-prettier@9.1.2(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1))(prettier@3.6.2) @@ -44504,7 +44577,7 @@ snapshots: tinyglobby: 0.2.15 unrs-resolver: 1.11.1 optionalDependencies: - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@2.6.1)) transitivePeerDependencies: - supports-color @@ -44518,12 +44591,12 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@6.21.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.1(jiti@1.21.7)): + eslint-module-utils@2.12.1(@typescript-eslint/parser@6.21.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.1(jiti@2.6.1)): dependencies: debug: 3.2.7 optionalDependencies: - '@typescript-eslint/parser': 6.21.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3) - eslint: 9.39.1(jiti@1.21.7) + '@typescript-eslint/parser': 6.21.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3) + eslint: 9.39.1(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 transitivePeerDependencies: - supports-color @@ -44560,6 +44633,20 @@ snapshots: transitivePeerDependencies: - supports-color + eslint-plugin-astro@1.5.0(eslint@9.39.1(jiti@1.21.7)): + dependencies: + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@1.21.7)) + '@jridgewell/sourcemap-codec': 1.5.5 + '@typescript-eslint/types': 8.48.0 + astro-eslint-parser: 1.2.2 + eslint: 9.39.1(jiti@1.21.7) + eslint-compat-utils: 0.6.5(eslint@9.39.1(jiti@1.21.7)) + globals: 16.5.0 + postcss: 8.5.6 + postcss-selector-parser: 7.1.0 + transitivePeerDependencies: + - supports-color + eslint-plugin-astro@1.5.0(eslint@9.39.1(jiti@2.6.1)): dependencies: '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@2.6.1)) @@ -44587,12 +44674,6 @@ snapshots: eslint-utils: 2.1.0 regexpp: 3.2.0 - eslint-plugin-es@3.0.1(eslint@9.39.1(jiti@1.21.7)): - dependencies: - eslint: 9.39.1(jiti@1.21.7) - eslint-utils: 2.1.0 - regexpp: 3.2.0 - eslint-plugin-es@3.0.1(eslint@9.39.1(jiti@2.6.1)): dependencies: eslint: 9.39.1(jiti@2.6.1) @@ -44646,7 +44727,7 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-plugin-import@2.32.0(@typescript-eslint/parser@6.21.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3))(eslint@9.39.1(jiti@1.21.7)): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@6.21.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3))(eslint@9.39.1(jiti@2.6.1)): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -44655,9 +44736,9 @@ snapshots: array.prototype.flatmap: 1.3.3 debug: 3.2.7 doctrine: 2.1.0 - eslint: 9.39.1(jiti@1.21.7) + eslint: 9.39.1(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@6.21.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.1(jiti@1.21.7)) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@6.21.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.1(jiti@2.6.1)) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -44669,7 +44750,7 @@ snapshots: string.prototype.trimend: 1.0.9 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 6.21.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3) + '@typescript-eslint/parser': 6.21.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack @@ -44704,7 +44785,7 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.1(jiti@2.6.1)): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@2.6.1)): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -44733,7 +44814,7 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1)): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@2.6.1)): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -44787,16 +44868,6 @@ snapshots: resolve: 1.22.11 semver: 6.3.1 - eslint-plugin-node@11.1.0(eslint@9.39.1(jiti@1.21.7)): - dependencies: - eslint: 9.39.1(jiti@1.21.7) - eslint-plugin-es: 3.0.1(eslint@9.39.1(jiti@1.21.7)) - eslint-utils: 2.1.0 - ignore: 5.3.2 - minimatch: 3.1.2 - resolve: 1.22.11 - semver: 6.3.1 - eslint-plugin-node@11.1.0(eslint@9.39.1(jiti@2.6.1)): dependencies: eslint: 9.39.1(jiti@2.6.1) @@ -44827,16 +44898,6 @@ snapshots: '@types/eslint': 9.6.1 eslint-config-prettier: 8.10.2(eslint@8.57.1) - eslint-plugin-prettier@5.5.4(@types/eslint@9.6.1)(eslint-config-prettier@8.10.2(eslint@9.39.1(jiti@1.21.7)))(eslint@9.39.1(jiti@1.21.7))(prettier@3.6.2): - dependencies: - eslint: 9.39.1(jiti@1.21.7) - prettier: 3.6.2 - prettier-linter-helpers: 1.0.0 - synckit: 0.11.11 - optionalDependencies: - '@types/eslint': 9.6.1 - eslint-config-prettier: 8.10.2(eslint@9.39.1(jiti@1.21.7)) - eslint-plugin-prettier@5.5.4(@types/eslint@9.6.1)(eslint-config-prettier@8.10.2(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1))(prettier@3.6.2): dependencies: eslint: 9.39.1(jiti@2.6.1) @@ -44861,10 +44922,6 @@ snapshots: dependencies: eslint: 8.57.1 - eslint-plugin-react-hooks@4.6.2(eslint@9.39.1(jiti@1.21.7)): - dependencies: - eslint: 9.39.1(jiti@1.21.7) - eslint-plugin-react-hooks@4.6.2(eslint@9.39.1(jiti@2.6.1)): dependencies: eslint: 9.39.1(jiti@2.6.1) @@ -44895,28 +44952,6 @@ snapshots: string.prototype.matchall: 4.0.12 string.prototype.repeat: 1.0.0 - eslint-plugin-react@7.37.5(eslint@9.39.1(jiti@1.21.7)): - dependencies: - array-includes: 3.1.9 - array.prototype.findlast: 1.2.5 - array.prototype.flatmap: 1.3.3 - array.prototype.tosorted: 1.1.4 - doctrine: 2.1.0 - es-iterator-helpers: 1.2.1 - eslint: 9.39.1(jiti@1.21.7) - estraverse: 5.3.0 - hasown: 2.0.2 - jsx-ast-utils: 3.3.5 - minimatch: 3.1.2 - object.entries: 1.1.9 - object.fromentries: 2.0.8 - object.values: 1.2.1 - prop-types: 15.8.1 - resolve: 2.0.0-next.5 - semver: 6.3.1 - string.prototype.matchall: 4.0.12 - string.prototype.repeat: 1.0.0 - eslint-plugin-react@7.37.5(eslint@9.39.1(jiti@2.6.1)): dependencies: array-includes: 3.1.9 @@ -45598,7 +45633,7 @@ snapshots: expo-dev-client@6.0.18(expo@54.0.25): dependencies: - expo: 54.0.25(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@6.1.2)(expo-router@6.0.15)(react-native-webview@13.12.2(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.25(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@6.1.2)(expo-router@6.0.15)(react-native-webview@13.12.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) expo-dev-launcher: 6.0.18(expo@54.0.25) expo-dev-menu: 7.0.17(expo@54.0.25) expo-dev-menu-interface: 2.0.0(expo@54.0.25) @@ -45647,7 +45682,7 @@ snapshots: expo-dev-launcher@6.0.18(expo@54.0.25): dependencies: - expo: 54.0.25(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@6.1.2)(expo-router@6.0.15)(react-native-webview@13.12.2(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.25(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@6.1.2)(expo-router@6.0.15)(react-native-webview@13.12.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) expo-dev-menu: 7.0.17(expo@54.0.25) expo-manifests: 1.0.9(expo@54.0.25) transitivePeerDependencies: @@ -45671,7 +45706,7 @@ snapshots: expo-dev-menu-interface@2.0.0(expo@54.0.25): dependencies: - expo: 54.0.25(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@6.1.2)(expo-router@6.0.15)(react-native-webview@13.12.2(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.25(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@6.1.2)(expo-router@6.0.15)(react-native-webview@13.12.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) expo-dev-menu@6.0.25(expo@52.0.47): dependencies: @@ -45695,7 +45730,7 @@ snapshots: expo-dev-menu@7.0.17(expo@54.0.25): dependencies: - expo: 54.0.25(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@6.1.2)(expo-router@6.0.15)(react-native-webview@13.12.2(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.25(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@6.1.2)(expo-router@6.0.15)(react-native-webview@13.12.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) expo-dev-menu-interface: 2.0.0(expo@54.0.25) expo-device@8.0.9(expo@54.0.13): @@ -45837,6 +45872,14 @@ snapshots: react: 19.1.0 react-native: 0.81.4(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0) + expo-font@55.0.4(expo@54.0.25)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.1.0))(react@19.1.0): + dependencies: + expo: 54.0.25(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@6.1.2)(expo-router@55.0.5)(react-native-webview@13.12.2(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.1.0))(react@19.1.0) + fontfaceobserver: 2.3.0 + react: 19.1.0 + react-native: 0.81.4(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.1.0) + optional: true + expo-font@55.0.4(expo@54.0.25)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1): dependencies: expo: 54.0.25(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@6.1.2)(expo-router@55.0.5)(react-native-webview@13.12.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1) @@ -45845,13 +45888,6 @@ snapshots: react-native: 0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1) optional: true - expo-font@55.0.4(expo@54.0.25)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0): - dependencies: - expo: 54.0.25(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@6.1.2)(expo-router@6.0.15)(react-native-webview@13.12.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) - fontfaceobserver: 2.3.0 - react: 19.1.0 - react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0) - expo-font@55.0.4(expo@55.0.5)(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0): dependencies: expo: 55.0.5(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@6.1.2)(expo-router@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native-webview@13.12.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) @@ -45992,7 +46028,7 @@ snapshots: expo-keep-awake@15.0.7(expo@54.0.25)(react@19.1.0): dependencies: - expo: 54.0.25(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@6.1.2)(expo-router@55.0.5)(react-native-webview@13.12.2(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.1.0))(react@19.1.0) + expo: 54.0.25(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@6.1.2)(expo-router@6.0.15)(react-native-webview@13.12.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) react: 19.1.0 expo-keep-awake@55.0.4(expo@55.0.5)(react@19.2.0): @@ -46168,7 +46204,7 @@ snapshots: expo-manifests@1.0.9(expo@54.0.25): dependencies: '@expo/config': 12.0.10 - expo: 54.0.25(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@6.1.2)(expo-router@6.0.15)(react-native-webview@13.12.2(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.25(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@6.1.2)(expo-router@6.0.15)(react-native-webview@13.12.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) expo-json-utils: 0.15.0 transitivePeerDependencies: - supports-color @@ -46361,6 +46397,57 @@ snapshots: - react-native - supports-color + expo-router@55.0.5(apnkrhypuo4jtg23v6qzhb7sxe): + dependencies: + '@expo/log-box': 55.0.7(@expo/dom-webview@55.0.3)(expo@55.0.5)(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) + '@expo/metro-runtime': 6.1.2(expo@55.0.5)(react-dom@19.2.4(react@19.2.4))(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) + '@expo/schema-utils': 55.0.2 + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.14)(react@19.2.4) + '@radix-ui/react-tabs': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@react-navigation/bottom-tabs': 7.15.5(@react-navigation/native@7.1.33(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native-safe-area-context@5.7.0(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native-screens@4.24.0(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) + '@react-navigation/native': 7.1.33(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) + '@react-navigation/native-stack': 7.14.4(@react-navigation/native@7.1.33(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native-safe-area-context@5.7.0(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native-screens@4.24.0(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) + client-only: 0.0.1 + debug: 4.4.3 + escape-string-regexp: 4.0.0 + expo: 55.0.5(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@6.1.2)(expo-router@55.0.5)(react-dom@19.2.4(react@19.2.4))(react-native-webview@13.12.2(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + expo-constants: 55.0.7(expo@55.0.5)(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(typescript@5.9.3) + expo-glass-effect: 55.0.8(expo@55.0.5)(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) + expo-image: 55.0.6(expo@55.0.5)(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) + expo-linking: 55.0.7(expo@55.0.5)(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + expo-server: 55.0.6 + expo-symbols: 55.0.5(expo-font@55.0.4)(expo@55.0.5)(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) + fast-deep-equal: 3.1.3 + invariant: 2.2.4 + nanoid: 3.3.11 + query-string: 7.1.3 + react: 19.2.4 + react-fast-compare: 3.2.2 + react-native: 0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4) + react-native-is-edge-to-edge: 1.2.1(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) + react-native-safe-area-context: 5.7.0(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) + react-native-screens: 4.24.0(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) + semver: 7.6.3 + server-only: 0.0.1 + sf-symbols-typescript: 2.2.0 + shallowequal: 1.1.0 + use-latest-callback: 0.2.6(react@19.2.4) + vaul: 1.1.2(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + optionalDependencies: + '@react-navigation/drawer': 7.7.4(@react-navigation/native@7.1.33(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native-gesture-handler@2.30.0(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native-reanimated@4.2.2(react-native-worklets@0.7.4(@babel/core@7.28.5)(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native-safe-area-context@5.7.0(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native-screens@4.24.0(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) + '@testing-library/react-native': 13.3.3(jest@30.3.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.19.12))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react-test-renderer@19.1.0(react@19.2.4))(react@19.2.4) + react-dom: 19.2.4(react@19.2.4) + react-native-gesture-handler: 2.30.0(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) + react-native-reanimated: 4.2.2(react-native-worklets@0.7.4(@babel/core@7.28.5)(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) + react-native-web: 0.21.2(encoding@0.1.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + transitivePeerDependencies: + - '@react-native-masked-view/masked-view' + - '@types/react' + - '@types/react-dom' + - expo-font + - supports-color + optional: true + expo-router@55.0.5(doxl36orx2qeblgfhuv3q6cufq): dependencies: '@expo/log-box': 55.0.7(@expo/dom-webview@55.0.3)(expo@54.0.25)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1) @@ -46412,7 +46499,7 @@ snapshots: - supports-color optional: true - expo-router@55.0.5(qwxmdxiornnsbyvrtivw4g2joq): + expo-router@55.0.5(xobbmdsfsaswzvlwz73gklusxm): dependencies: '@expo/log-box': 55.0.7(@expo/dom-webview@55.0.3)(expo@54.0.25)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.1.0))(react@19.1.0) '@expo/metro-runtime': 6.1.2(expo@54.0.25)(react-dom@19.2.4(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.1.0))(react@19.1.0) @@ -46431,7 +46518,7 @@ snapshots: expo-image: 55.0.6(expo@54.0.25)(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.2.4(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.1.0))(react@19.1.0) expo-linking: 55.0.7(expo@54.0.25)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.1.0))(react@19.1.0)(typescript@5.9.3) expo-server: 55.0.6 - expo-symbols: 55.0.5(expo-font@14.0.10)(expo@54.0.25)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.1.0))(react@19.1.0) + expo-symbols: 55.0.5(expo-font@55.0.4)(expo@54.0.25)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.1.0))(react@19.1.0) fast-deep-equal: 3.1.3 invariant: 2.2.4 nanoid: 3.3.11 @@ -46463,57 +46550,6 @@ snapshots: - supports-color optional: true - expo-router@55.0.5(tkph4mqwn7yyg5tlp6kukooce4): - dependencies: - '@expo/log-box': 55.0.7(@expo/dom-webview@55.0.3)(expo@55.0.5)(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) - '@expo/metro-runtime': 6.1.2(expo@55.0.5)(react-dom@19.2.4(react@19.2.4))(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) - '@expo/schema-utils': 55.0.2 - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.14)(react@19.2.4) - '@radix-ui/react-tabs': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@react-navigation/bottom-tabs': 7.15.5(@react-navigation/native@7.1.33(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native-safe-area-context@5.7.0(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native-screens@4.24.0(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) - '@react-navigation/native': 7.1.33(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) - '@react-navigation/native-stack': 7.14.4(@react-navigation/native@7.1.33(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native-safe-area-context@5.7.0(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native-screens@4.24.0(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) - client-only: 0.0.1 - debug: 4.4.3 - escape-string-regexp: 4.0.0 - expo: 55.0.5(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@6.1.2)(expo-router@55.0.5)(react-dom@19.2.4(react@19.2.4))(react-native-webview@13.12.2(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) - expo-constants: 55.0.7(expo@55.0.5)(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(typescript@5.9.3) - expo-glass-effect: 55.0.8(expo@55.0.5)(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) - expo-image: 55.0.6(expo@55.0.5)(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) - expo-linking: 55.0.7(expo@55.0.5)(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) - expo-server: 55.0.6 - expo-symbols: 55.0.5(expo-font@55.0.4)(expo@55.0.5)(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) - fast-deep-equal: 3.1.3 - invariant: 2.2.4 - nanoid: 3.3.11 - query-string: 7.1.3 - react: 19.2.4 - react-fast-compare: 3.2.2 - react-native: 0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4) - react-native-is-edge-to-edge: 1.2.1(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) - react-native-safe-area-context: 5.7.0(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) - react-native-screens: 4.24.0(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) - semver: 7.6.3 - server-only: 0.0.1 - sf-symbols-typescript: 2.2.0 - shallowequal: 1.1.0 - use-latest-callback: 0.2.6(react@19.2.4) - vaul: 1.1.2(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - optionalDependencies: - '@react-navigation/drawer': 7.7.4(@react-navigation/native@7.1.33(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native-gesture-handler@2.30.0(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native-reanimated@4.2.2(react-native-worklets@0.7.4(@babel/core@7.28.5)(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native-safe-area-context@5.7.0(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native-screens@4.24.0(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) - '@testing-library/react-native': 13.3.3(jest@30.3.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.19.12)))(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react-test-renderer@19.1.0(react@19.2.4))(react@19.2.4) - react-dom: 19.2.4(react@19.2.4) - react-native-gesture-handler: 2.30.0(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) - react-native-reanimated: 4.2.2(react-native-worklets@0.7.4(@babel/core@7.28.5)(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) - react-native-web: 0.21.2(encoding@0.1.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - transitivePeerDependencies: - - '@react-native-masked-view/masked-view' - - '@types/react' - - '@types/react-dom' - - expo-font - - supports-color - optional: true - expo-router@55.0.5(xtsqo6xlpeezoeb4r7ibrbxkam): dependencies: '@expo/log-box': 55.0.7(@expo/dom-webview@55.0.3)(expo@55.0.5)(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0) @@ -46840,11 +46876,11 @@ snapshots: react-native: 0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0) sf-symbols-typescript: 2.1.0 - expo-symbols@55.0.5(expo-font@14.0.10)(expo@54.0.25)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.1.0))(react@19.1.0): + expo-symbols@55.0.5(expo-font@55.0.4)(expo@54.0.25)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.1.0))(react@19.1.0): dependencies: '@expo-google-fonts/material-symbols': 0.4.25 expo: 54.0.25(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@6.1.2)(expo-router@55.0.5)(react-native-webview@13.12.2(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.1.0))(react@19.1.0) - expo-font: 14.0.10(expo@54.0.25)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.1.0))(react@19.1.0) + expo-font: 55.0.4(expo@54.0.25)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.1.0))(react@19.1.0) react: 19.1.0 react-native: 0.81.4(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.1.0) sf-symbols-typescript: 2.2.0 @@ -46972,7 +47008,7 @@ snapshots: expo-updates-interface@2.0.0(expo@54.0.25): dependencies: - expo: 54.0.25(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@6.1.2)(expo-router@6.0.15)(react-native-webview@13.12.2(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.25(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@6.1.2)(expo-router@6.0.15)(react-native-webview@13.12.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) expo-updates@29.0.13(expo@54.0.13)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0): dependencies: @@ -47186,7 +47222,7 @@ snapshots: '@expo/fingerprint': 0.15.3 '@expo/metro': 54.1.0 '@expo/metro-config': 54.0.9(expo@54.0.25) - '@expo/vector-icons': 15.0.3(expo-font@14.0.10)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.1.0))(react@19.1.0) + '@expo/vector-icons': 15.0.3(expo-font@14.0.10(expo@54.0.25)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.1.0))(react@19.1.0) '@ungap/structured-clone': 1.3.0 babel-preset-expo: 54.0.7(@babel/core@7.28.5)(@babel/runtime@7.28.4)(expo@54.0.25)(react-refresh@0.14.2) expo-asset: 12.0.10(expo@54.0.25)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.1.0))(react@19.1.0) @@ -49371,16 +49407,16 @@ snapshots: - supports-color - ts-node - jest-cli@29.7.0(@types/node@24.10.1)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)): + jest-cli@29.7.0(@types/node@24.10.1): dependencies: - '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)) + '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)) '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 chalk: 4.1.2 - create-jest: 29.7.0(@types/node@24.10.1)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)) + create-jest: 29.7.0(@types/node@24.10.1) exit: 0.1.2 import-local: 3.2.0 - jest-config: 29.7.0(@types/node@24.10.1)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)) + jest-config: 29.7.0(@types/node@24.10.1) jest-util: 29.7.0 jest-validate: 29.7.0 yargs: 17.7.2 @@ -49411,7 +49447,7 @@ snapshots: jest-cli@30.3.0(@types/node@20.19.25)(esbuild-register@3.6.0(esbuild@0.27.4)): dependencies: - '@jest/core': 30.3.0(esbuild-register@3.6.0(esbuild@0.27.4)) + '@jest/core': 30.3.0(esbuild-register@3.6.0(esbuild@0.27.4))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)) '@jest/test-result': 30.3.0 '@jest/types': 30.3.0 chalk: 4.1.2 @@ -49429,15 +49465,15 @@ snapshots: - ts-node optional: true - jest-cli@30.3.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.19.12)): + jest-cli@30.3.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.19.12))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)): dependencies: - '@jest/core': 30.3.0(esbuild-register@3.6.0(esbuild@0.19.12)) + '@jest/core': 30.3.0(esbuild-register@3.6.0(esbuild@0.19.12))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)) '@jest/test-result': 30.3.0 '@jest/types': 30.3.0 chalk: 4.1.2 exit-x: 0.2.2 import-local: 3.2.0 - jest-config: 30.3.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.19.12)) + jest-config: 30.3.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.19.12))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)) jest-util: 30.3.0 jest-validate: 30.3.0 yargs: 17.7.2 @@ -49447,17 +49483,16 @@ snapshots: - esbuild-register - supports-color - ts-node - optional: true jest-cli@30.3.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.27.4)): dependencies: - '@jest/core': 30.3.0(esbuild-register@3.6.0(esbuild@0.27.4)) + '@jest/core': 30.3.0(esbuild-register@3.6.0(esbuild@0.27.4))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)) '@jest/test-result': 30.3.0 '@jest/types': 30.3.0 chalk: 4.1.2 exit-x: 0.2.2 import-local: 3.2.0 - jest-config: 30.3.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.27.4)) + jest-config: 30.3.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.27.4))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)) jest-util: 30.3.0 jest-validate: 30.3.0 yargs: 17.7.2 @@ -49487,26 +49522,6 @@ snapshots: - supports-color - ts-node - jest-cli@30.3.0(esbuild-register@3.6.0(esbuild@0.27.4)): - dependencies: - '@jest/core': 30.3.0(esbuild-register@3.6.0(esbuild@0.27.4)) - '@jest/test-result': 30.3.0 - '@jest/types': 30.3.0 - chalk: 4.1.2 - exit-x: 0.2.2 - import-local: 3.2.0 - jest-config: 30.3.0(esbuild-register@3.6.0(esbuild@0.27.4)) - jest-util: 30.3.0 - jest-validate: 30.3.0 - yargs: 17.7.2 - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - esbuild-register - - supports-color - - ts-node - optional: true - jest-config@29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)): dependencies: '@babel/core': 7.28.5 @@ -49538,38 +49553,7 @@ snapshots: - babel-plugin-macros - supports-color - jest-config@29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)): - dependencies: - '@babel/core': 7.28.5 - '@jest/test-sequencer': 29.7.0 - '@jest/types': 29.6.3 - babel-jest: 29.7.0(@babel/core@7.28.5) - chalk: 4.1.2 - ci-info: 3.9.0 - deepmerge: 4.3.1 - glob: 7.2.3 - graceful-fs: 4.2.11 - jest-circus: 29.7.0 - jest-environment-node: 29.7.0 - jest-get-type: 29.6.3 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-runner: 29.7.0 - jest-util: 29.7.0 - jest-validate: 29.7.0 - micromatch: 4.0.8 - parse-json: 5.2.0 - pretty-format: 29.7.0 - slash: 3.0.0 - strip-json-comments: 3.1.1 - optionalDependencies: - '@types/node': 22.19.1 - ts-node: 10.9.2(@types/node@24.10.1)(typescript@5.9.3) - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - - jest-config@29.7.0(@types/node@24.10.1)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)): + jest-config@29.7.0(@types/node@24.10.1): dependencies: '@babel/core': 7.28.5 '@jest/test-sequencer': 29.7.0 @@ -49595,7 +49579,6 @@ snapshots: strip-json-comments: 3.1.1 optionalDependencies: '@types/node': 24.10.1 - ts-node: 10.9.2(@types/node@24.10.1)(typescript@5.9.3) transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -49667,7 +49650,7 @@ snapshots: - supports-color optional: true - jest-config@30.3.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.19.12)): + jest-config@30.3.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.19.12))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)): dependencies: '@babel/core': 7.28.5 '@jest/get-type': 30.1.0 @@ -49695,39 +49678,7 @@ snapshots: optionalDependencies: '@types/node': 22.19.1 esbuild-register: 3.6.0(esbuild@0.19.12) - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - optional: true - - jest-config@30.3.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.27.4)): - dependencies: - '@babel/core': 7.28.5 - '@jest/get-type': 30.1.0 - '@jest/pattern': 30.0.1 - '@jest/test-sequencer': 30.3.0 - '@jest/types': 30.3.0 - babel-jest: 30.3.0(@babel/core@7.28.5) - chalk: 4.1.2 - ci-info: 4.3.1 - deepmerge: 4.3.1 - glob: 10.5.0 - graceful-fs: 4.2.11 - jest-circus: 30.3.0 - jest-docblock: 30.2.0 - jest-environment-node: 30.3.0 - jest-regex-util: 30.0.1 - jest-resolve: 30.3.0 - jest-runner: 30.3.0 - jest-util: 30.3.0 - jest-validate: 30.3.0 - parse-json: 5.2.0 - pretty-format: 30.3.0 - slash: 3.0.0 - strip-json-comments: 3.1.1 - optionalDependencies: - '@types/node': 22.19.1 - esbuild-register: 3.6.0(esbuild@0.27.4) + ts-node: 10.9.2(@types/node@22.19.1)(typescript@5.9.3) transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -49765,38 +49716,6 @@ snapshots: - babel-plugin-macros - supports-color - jest-config@30.3.0(esbuild-register@3.6.0(esbuild@0.27.4)): - dependencies: - '@babel/core': 7.28.5 - '@jest/get-type': 30.1.0 - '@jest/pattern': 30.0.1 - '@jest/test-sequencer': 30.3.0 - '@jest/types': 30.3.0 - babel-jest: 30.3.0(@babel/core@7.28.5) - chalk: 4.1.2 - ci-info: 4.3.1 - deepmerge: 4.3.1 - glob: 10.5.0 - graceful-fs: 4.2.11 - jest-circus: 30.3.0 - jest-docblock: 30.2.0 - jest-environment-node: 30.3.0 - jest-regex-util: 30.0.1 - jest-resolve: 30.3.0 - jest-runner: 30.3.0 - jest-util: 30.3.0 - jest-validate: 30.3.0 - parse-json: 5.2.0 - pretty-format: 30.3.0 - slash: 3.0.0 - strip-json-comments: 3.1.1 - optionalDependencies: - esbuild-register: 3.6.0(esbuild@0.27.4) - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - optional: true - jest-diff@29.7.0: dependencies: chalk: 4.1.2 @@ -50454,12 +50373,12 @@ snapshots: - supports-color - ts-node - jest@29.7.0(@types/node@24.10.1)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)): + jest@29.7.0(@types/node@24.10.1): dependencies: - '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)) + '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)) '@jest/types': 29.6.3 import-local: 3.2.0 - jest-cli: 29.7.0(@types/node@24.10.1)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)) + jest-cli: 29.7.0(@types/node@24.10.1) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -50481,7 +50400,7 @@ snapshots: jest@30.3.0(@types/node@20.19.25)(esbuild-register@3.6.0(esbuild@0.27.4)): dependencies: - '@jest/core': 30.3.0(esbuild-register@3.6.0(esbuild@0.27.4)) + '@jest/core': 30.3.0(esbuild-register@3.6.0(esbuild@0.27.4))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)) '@jest/types': 30.3.0 import-local: 3.2.0 jest-cli: 30.3.0(@types/node@20.19.25)(esbuild-register@3.6.0(esbuild@0.27.4)) @@ -50493,23 +50412,22 @@ snapshots: - ts-node optional: true - jest@30.3.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.19.12)): + jest@30.3.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.19.12))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)): dependencies: - '@jest/core': 30.3.0(esbuild-register@3.6.0(esbuild@0.19.12)) + '@jest/core': 30.3.0(esbuild-register@3.6.0(esbuild@0.19.12))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)) '@jest/types': 30.3.0 import-local: 3.2.0 - jest-cli: 30.3.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.19.12)) + jest-cli: 30.3.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.19.12))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)) transitivePeerDependencies: - '@types/node' - babel-plugin-macros - esbuild-register - supports-color - ts-node - optional: true jest@30.3.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.27.4)): dependencies: - '@jest/core': 30.3.0(esbuild-register@3.6.0(esbuild@0.27.4)) + '@jest/core': 30.3.0(esbuild-register@3.6.0(esbuild@0.27.4))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)) '@jest/types': 30.3.0 import-local: 3.2.0 jest-cli: 30.3.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.27.4)) @@ -50533,20 +50451,6 @@ snapshots: - supports-color - ts-node - jest@30.3.0(esbuild-register@3.6.0(esbuild@0.27.4)): - dependencies: - '@jest/core': 30.3.0(esbuild-register@3.6.0(esbuild@0.27.4)) - '@jest/types': 30.3.0 - import-local: 3.2.0 - jest-cli: 30.3.0(esbuild-register@3.6.0(esbuild@0.27.4)) - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - esbuild-register - - supports-color - - ts-node - optional: true - jimp-compact@0.16.1: {} jiti@1.21.7: {} @@ -50847,6 +50751,10 @@ snapshots: leac@0.6.0: {} + leaflet.markercluster@1.5.3(leaflet@1.9.4): + dependencies: + leaflet: 1.9.4 + leaflet@1.9.4: {} leven@3.1.0: {} @@ -57502,7 +57410,28 @@ snapshots: ts-interface-checker@0.1.13: {} - ts-jest@29.4.5(@babel/core@7.28.5)(@jest/transform@30.3.0)(@jest/types@30.3.0)(babel-jest@30.3.0(@babel/core@7.28.5))(esbuild@0.27.4)(jest-util@30.3.0)(jest@30.2.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.27.4))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))(typescript@5.9.3): + ts-jest@29.4.5(@babel/core@7.28.5)(@jest/transform@30.3.0)(@jest/types@30.3.0)(babel-jest@30.3.0(@babel/core@7.28.5))(esbuild@0.19.12)(jest-util@30.3.0)(jest@30.3.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.19.12))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))(typescript@5.9.3): + dependencies: + bs-logger: 0.2.6 + fast-json-stable-stringify: 2.1.0 + handlebars: 4.7.8 + jest: 30.3.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.19.12))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)) + json5: 2.2.3 + lodash.memoize: 4.1.2 + make-error: 1.3.6 + semver: 7.7.3 + type-fest: 4.41.0 + typescript: 5.9.3 + yargs-parser: 21.1.1 + optionalDependencies: + '@babel/core': 7.28.5 + '@jest/transform': 30.3.0 + '@jest/types': 30.3.0 + babel-jest: 30.3.0(@babel/core@7.28.5) + esbuild: 0.19.12 + jest-util: 30.3.0 + + ts-jest@29.4.5(@babel/core@7.28.5)(@jest/transform@30.3.0)(@jest/types@30.3.0)(babel-jest@30.3.0(@babel/core@7.28.5))(esbuild@0.27.4)(jest-util@30.3.0)(jest@30.2.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.27.4)))(typescript@5.9.3): dependencies: bs-logger: 0.2.6 fast-json-stable-stringify: 2.1.0 @@ -57605,6 +57534,16 @@ snapshots: typescript: 5.9.3 webpack: 5.100.2 + ts-loader@9.5.4(typescript@5.9.3)(webpack@5.97.1(esbuild@0.19.12)): + dependencies: + chalk: 4.1.2 + enhanced-resolve: 5.18.3 + micromatch: 4.0.8 + semver: 7.7.3 + source-map: 0.7.6 + typescript: 5.9.3 + webpack: 5.97.1(esbuild@0.19.12) + ts-node@10.9.2(@types/node@20.19.25)(typescript@5.9.3): dependencies: '@cspotcode/source-map-support': 0.8.1 @@ -58544,6 +58483,23 @@ snapshots: lightningcss: 1.30.2 terser: 5.44.1 + vite@6.4.1(@types/node@20.19.25)(jiti@1.21.7)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.1): + dependencies: + esbuild: 0.25.12 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.6 + rollup: 4.53.3 + tinyglobby: 0.2.15 + optionalDependencies: + '@types/node': 20.19.25 + fsevents: 2.3.3 + jiti: 1.21.7 + lightningcss: 1.30.2 + terser: 5.44.1 + tsx: 4.21.0 + yaml: 2.8.1 + vite@6.4.1(@types/node@20.19.25)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.1): dependencies: esbuild: 0.25.12 @@ -58647,6 +58603,10 @@ snapshots: tsx: 4.21.0 yaml: 2.8.1 + vitefu@1.1.1(vite@6.4.1(@types/node@20.19.25)(jiti@1.21.7)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.1)): + optionalDependencies: + vite: 6.4.1(@types/node@20.19.25)(jiti@1.21.7)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.1) + vitefu@1.1.1(vite@6.4.1(@types/node@20.19.25)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.1)): optionalDependencies: vite: 6.4.1(@types/node@20.19.25)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.1) @@ -58827,7 +58787,7 @@ snapshots: '@vitest/utils': 3.2.4 chai: 5.3.3 debug: 4.4.3 - expect-type: 1.2.2 + expect-type: 1.3.0 magic-string: 0.30.21 pathe: 2.0.3 picomatch: 4.0.3 @@ -58860,6 +58820,51 @@ snapshots: - tsx - yaml + vitest@3.2.4(@types/debug@4.1.12)(@types/node@20.19.25)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(jiti@2.6.1)(jsdom@27.2.0)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.1): + dependencies: + '@types/chai': 5.2.3 + '@vitest/expect': 3.2.4 + '@vitest/mocker': 3.2.4(vite@6.4.1(@types/node@20.19.25)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.1)) + '@vitest/pretty-format': 3.2.4 + '@vitest/runner': 3.2.4 + '@vitest/snapshot': 3.2.4 + '@vitest/spy': 3.2.4 + '@vitest/utils': 3.2.4 + chai: 5.3.3 + debug: 4.4.3 + expect-type: 1.3.0 + magic-string: 0.30.21 + pathe: 2.0.3 + picomatch: 4.0.3 + std-env: 3.10.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinyglobby: 0.2.15 + tinypool: 1.1.1 + tinyrainbow: 2.0.0 + vite: 6.4.1(@types/node@20.19.25)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.1) + vite-node: 3.2.4(@types/node@20.19.25)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.1) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/debug': 4.1.12 + '@types/node': 20.19.25 + '@vitest/browser': 3.2.4(playwright@1.57.0)(vite@6.4.1(@types/node@20.19.25)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.1))(vitest@3.2.4) + '@vitest/ui': 3.2.4(vitest@3.2.4) + jsdom: 27.2.0 + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(jiti@2.6.1)(jsdom@27.2.0)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.1): dependencies: '@types/chai': 5.2.3 @@ -58872,7 +58877,7 @@ snapshots: '@vitest/utils': 3.2.4 chai: 5.3.3 debug: 4.4.3 - expect-type: 1.2.2 + expect-type: 1.3.0 magic-string: 0.30.21 pathe: 2.0.3 picomatch: 4.0.3 @@ -58888,7 +58893,7 @@ snapshots: optionalDependencies: '@types/debug': 4.1.12 '@types/node': 24.10.1 - '@vitest/browser': 3.2.4(playwright@1.57.0)(vite@6.4.1(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.1))(vitest@3.2.4) + '@vitest/browser': 3.2.4(playwright@1.57.0)(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.1))(vitest@3.2.4) '@vitest/ui': 3.2.4(vitest@3.2.4) jsdom: 27.2.0 transitivePeerDependencies: