diff --git a/.github/workflows/backend-ci.yml b/.github/workflows/backend-ci.yml index 163d611c..46ad32bc 100644 --- a/.github/workflows/backend-ci.yml +++ b/.github/workflows/backend-ci.yml @@ -25,7 +25,7 @@ jobs: cache: npm - name: Install dependencies - run: npm ci + run: npm ci --legacy-peer-deps - name: Check openapi.json is in sync with TypeScript spec source run: npm run openapi:check @@ -68,7 +68,7 @@ jobs: cache: npm - name: Install dependencies - run: npm ci + run: npm ci --legacy-peer-deps - name: Run backend tests with coverage id: test diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b51d16b2..73dfd701 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,7 +36,7 @@ jobs: cache: npm - name: Install dependencies - run: npm ci + run: npm ci --legacy-peer-deps - name: Validate migrations run: npm run migrations:validate @@ -78,7 +78,7 @@ jobs: cache: npm - name: Install dependencies - run: npm ci + run: npm ci --legacy-peer-deps - name: Run migration rollback tests run: npm run test:migrations @@ -111,7 +111,7 @@ jobs: cache: npm - name: Install dependencies - run: npm ci + run: npm ci --legacy-peer-deps - name: Run tests with coverage run: npm run test:ci @@ -140,7 +140,7 @@ jobs: cache: npm - name: Install dependencies - run: npm ci + run: npm ci --legacy-peer-deps - name: Run npm audit run: npm run audit:high @@ -162,7 +162,7 @@ jobs: cache: npm - name: Install dependencies - run: npm ci + run: npm ci --legacy-peer-deps - name: Build Storybook run: npm run storybook:ci @@ -211,7 +211,7 @@ jobs: cache: npm - name: Install dependencies - run: npm ci + run: npm ci --legacy-peer-deps - name: Apply migrations run: npm run migrate diff --git a/backend/docs/openapi.json b/backend/docs/openapi.json index da56442c..1297b7d0 100644 --- a/backend/docs/openapi.json +++ b/backend/docs/openapi.json @@ -3,845 +3,843 @@ "info": { "title": "PetChain API", "version": "1.0.0", - "description": "REST API for PetChain — secure pet health records, medication reminders, QR scanning, appointments, payments, and blockchain-verified medical records.", + "description": "## Overview\n\nThe PetChain API powers the PetChain mobile app — a blockchain-backed pet health management platform.\n\n### Key capabilities\n- **Pet management** — register pets, upload photos, look up by QR code\n- **Medical records** — create and retrieve vet visit records with rich structured data\n- **Blockchain verification** — anchor medical record hashes on the Stellar network for tamper-proof integrity\n- **Appointments** — book, reschedule, and cancel vet appointments\n- **Medications** — track medication schedules and dosing history\n- **Offline sync** — queue-based sync with conflict resolution for offline-first mobile use\n\n### Authentication\n\nAll protected endpoints require a JWT access token in the `Authorization` header:\n\n```\nAuthorization: Bearer \n```\n\nTokens are obtained via `POST /auth/login` or `POST /auth/register`.\nWhen a token expires, the client automatically calls `POST /auth/refresh` with the stored refresh token.\n\n### Rate limiting\n\n- Login/register endpoints: 10 requests per minute per IP\n- All other endpoints: 100 requests per minute per user\n\n### Error format\n\nAll errors follow a consistent shape:\n```json\n{\n \"success\": false,\n \"error\": {\n \"code\": \"VALIDATION_ERROR\",\n \"message\": \"Human-readable description\",\n \"details\": { \"field\": \"email\", \"issue\": \"Invalid format\" }\n },\n \"timestamp\": \"2024-01-15T10:30:00Z\"\n}\n```", "contact": { - "name": "PetChain Support", - "email": "support@petchain.app", + "name": "PetChain Engineering", + "email": "engineering@petchain.app", "url": "https://petchain.app" }, "license": { - "name": "MIT" + "name": "MIT", + "url": "https://opensource.org/licenses/MIT" } }, "servers": [ { "url": "http://localhost:3000/api", - "description": "Local development" + "description": "Local development server" + }, + { + "url": "https://staging.petchain.app/api", + "description": "Staging environment" }, { "url": "https://api.petchain.app/api", - "description": "Production" + "description": "Production environment" } ], "tags": [ { - "name": "Health", - "description": "Service health check" - }, - { - "name": "Auth", - "description": "Authentication — login, register, token refresh" + "name": "Authentication", + "description": "Login, register, token refresh, OAuth, and password management" }, { "name": "Users", - "description": "User management" + "description": "User profile management and role-based access" }, { "name": "Pets", - "description": "Pet profiles and QR lookup" + "description": "Pet registration, profile management, and QR code lookup" }, { "name": "Medical Records", - "description": "Veterinary medical records with optional blockchain verification" + "description": "Vet visit records with blockchain-backed integrity verification" }, { "name": "Appointments", - "description": "Appointment scheduling" + "description": "Vet appointment scheduling and management" }, { "name": "Medications", - "description": "Medication tracking" - }, - { - "name": "Payments", - "description": "Subscription plans and payment processing (stubbed)" - }, - { - "name": "Analytics", - "description": "Usage analytics events and dashboard" + "description": "Medication schedules and dosing tracking" }, { - "name": "Audit Logs", - "description": "Admin-only audit trail" - }, - { - "name": "Backups", - "description": "User data backup and restore" - }, - { - "name": "Community", - "description": "Community posts and tips" - }, - { - "name": "Import", - "description": "Bulk CSV import" + "name": "Blockchain", + "description": "Stellar blockchain integration for tamper-proof medical record anchoring" } ], - "components": { - "securitySchemes": { - "BearerAuth": { - "type": "http", - "scheme": "bearer", - "bearerFormat": "JWT", - "description": "JWT token obtained from /auth/login. In development, use mock-{userId} format." - } - }, - "schemas": { - "ApiResponse": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "example": true - }, - "data": {}, - "message": { - "type": "string" - }, - "timestamp": { - "type": "string", - "format": "date-time" - } - }, - "required": [ - "success", - "data", - "timestamp" - ] - }, - "ApiError": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "example": false - }, - "error": { - "type": "object", - "properties": { - "code": { - "type": "string", - "example": "NOT_FOUND" - }, - "message": { - "type": "string", - "example": "Resource not found" + "paths": { + "/auth/login": { + "post": { + "tags": ["Authentication"], + "summary": "Login with email and password", + "description": "Authenticates a user with email/password credentials. Returns a short-lived JWT access token and a long-lived refresh token. Store both tokens securely (Keychain on iOS, SecureStore on Android).", + "operationId": "loginUser", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LoginRequest" }, - "details": { - "type": "object" + "examples": { + "petOwner": { + "summary": "Pet owner login", + "value": { + "email": "jane.doe@example.com", + "password": "SecurePass123!" + } + }, + "vet": { + "summary": "Veterinarian login", + "value": { + "email": "dr.johnson@vetclinic.com", + "password": "VetPass456!" + } + } } - }, - "required": [ - "code", - "message" - ] - }, - "timestamp": { - "type": "string", - "format": "date-time" + } } }, - "required": [ - "success", - "error", - "timestamp" - ] - }, - "PaginationMeta": { - "type": "object", - "properties": { - "page": { - "type": "integer", - "example": 1 - }, - "limit": { - "type": "integer", - "example": 20 + "responses": { + "200": { + "description": "Login successful", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LoginResponse" + }, + "example": { + "user": { + "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "email": "jane.doe@example.com", + "name": "Jane Doe", + "role": "owner" + }, + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", + "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", + "expiresIn": 604800 + } + } + } }, - "total": { - "type": "integer", - "example": 100 + "400": { + "$ref": "#/components/responses/BadRequest" }, - "totalPages": { - "type": "integer", - "example": 5 + "401": { + "$ref": "#/components/responses/Unauthorized" }, - "hasNext": { - "type": "boolean" + "429": { + "$ref": "#/components/responses/TooManyRequests" }, - "hasPrev": { - "type": "boolean" + "500": { + "$ref": "#/components/responses/InternalServerError" } } - }, - "User": { - "type": "object", - "properties": { - "id": { - "type": "string", - "format": "uuid" + } + }, + "/auth/register": { + "post": { + "tags": ["Authentication"], + "summary": "Register a new user account", + "description": "Creates a new user account. Sends a verification email to the provided address. Returns JWT tokens immediately so the user can start using the app before verifying their email.", + "operationId": "registerUser", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RegisterRequest" + }, + "example": { + "email": "jane.doe@example.com", + "name": "Jane Doe", + "password": "SecurePass123!", + "phone": "+14155552671", + "role": "owner" + } + } + } + }, + "responses": { + "201": { + "description": "Account created successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RegisterResponse" + } + } + } }, - "email": { - "type": "string", - "format": "email" + "400": { + "$ref": "#/components/responses/BadRequest" }, - "name": { - "type": "string" + "409": { + "description": "Email already registered", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + }, + "example": { + "success": false, + "error": { + "code": "EMAIL_ALREADY_EXISTS", + "message": "An account with this email already exists" + }, + "timestamp": "2024-01-15T10:30:00Z" + } + } + } }, - "phone": { - "type": "string" + "500": { + "$ref": "#/components/responses/InternalServerError" + } + } + } + }, + "/auth/refresh": { + "post": { + "tags": ["Authentication"], + "summary": "Refresh access token", + "description": "Exchanges a valid refresh token for a new access token. The old refresh token is invalidated. Called automatically by the API client interceptor on 401 responses.", + "operationId": "refreshToken", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RefreshTokenRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Token refreshed successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RefreshTokenResponse" + } + } + } }, - "role": { - "type": "string", - "enum": [ - "owner", - "vet", - "admin" - ] + "400": { + "$ref": "#/components/responses/BadRequest" }, - "pets": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string" + "401": { + "description": "Refresh token expired or invalid — user must log in again", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" }, - "name": { - "type": "string" + "example": { + "success": false, + "error": { + "code": "REFRESH_TOKEN_EXPIRED", + "message": "Session expired — please log in again" + }, + "timestamp": "2024-01-15T10:30:00Z" } } } }, - "isEmailVerified": { - "type": "boolean" - }, - "lastLoginAt": { - "type": "string", - "format": "date-time" + "500": { + "$ref": "#/components/responses/InternalServerError" + } + } + } + }, + "/auth/logout": { + "post": { + "tags": ["Authentication"], + "summary": "Logout and invalidate session", + "description": "Invalidates the server-side session. The client should also clear locally stored tokens. This is a best-effort call — local token cleanup always proceeds even if this request fails.", + "operationId": "logoutUser", + "security": [ + { + "BearerAuth": [] + } + ], + "responses": { + "204": { + "description": "Logged out successfully (no content)" }, - "createdAt": { - "type": "string", - "format": "date-time" + "401": { + "$ref": "#/components/responses/Unauthorized" }, - "updatedAt": { - "type": "string", - "format": "date-time" + "500": { + "$ref": "#/components/responses/InternalServerError" } } - }, - "Pet": { - "type": "object", - "properties": { - "id": { - "type": "string", - "format": "uuid" - }, - "name": { - "type": "string", - "example": "Buddy" + } + }, + "/auth/verify-email": { + "post": { + "tags": ["Authentication"], + "summary": "Verify email address", + "description": "Verifies the user's email address using the token sent to their inbox.", + "operationId": "verifyEmail", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/VerifyEmailRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Email verified successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + }, + "example": { + "success": true, + "data": null, + "message": "Email verified successfully", + "timestamp": "2024-01-15T10:30:00Z" + } + } + } }, - "species": { - "type": "string", - "enum": [ - "dog", - "cat", - "bird", - "rabbit", - "other" - ], - "example": "dog" + "400": { + "$ref": "#/components/responses/BadRequest" }, - "breed": { - "type": "string", - "example": "Labrador" + "404": { + "$ref": "#/components/responses/NotFound" }, - "dateOfBirth": { - "type": "string", - "format": "date", - "example": "2020-01-15" - }, - "microchipId": { - "type": "string", - "example": "CHIP-001" - }, - "photoUrl": { - "type": "string", - "format": "uri" + "500": { + "$ref": "#/components/responses/InternalServerError" + } + } + } + }, + "/auth/forgot-password": { + "post": { + "tags": ["Authentication"], + "summary": "Request password reset email", + "description": "Sends a password reset link to the provided email address. Always returns 200 to prevent email enumeration attacks.", + "operationId": "forgotPassword", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ForgotPasswordRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Reset email sent (or silently ignored if email not found)", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + }, + "example": { + "success": true, + "data": null, + "message": "If that email is registered, a reset link has been sent", + "timestamp": "2024-01-15T10:30:00Z" + } + } + } }, - "thumbnailUrl": { - "type": "string", - "format": "uri" + "400": { + "$ref": "#/components/responses/BadRequest" }, - "ownerId": { - "type": "string", - "format": "uuid" + "429": { + "$ref": "#/components/responses/TooManyRequests" }, - "owner": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "email": { - "type": "string" + "500": { + "$ref": "#/components/responses/InternalServerError" + } + } + } + }, + "/auth/reset-password": { + "post": { + "tags": ["Authentication"], + "summary": "Reset password with token", + "description": "Resets the user's password using the token from the reset email.", + "operationId": "resetPassword", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ResetPasswordRequest" } } - }, - "createdAt": { - "type": "string", - "format": "date-time" - }, - "updatedAt": { - "type": "string", - "format": "date-time" } }, - "required": [ - "id", - "name", - "species", - "ownerId" - ] - }, - "MedicalRecord": { - "type": "object", - "properties": { - "id": { - "type": "string", - "format": "uuid" + "responses": { + "200": { + "description": "Password reset successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + } + } }, - "petId": { - "type": "string", - "format": "uuid" + "400": { + "$ref": "#/components/responses/BadRequest" }, - "vetId": { - "type": "string", - "format": "uuid" + "404": { + "$ref": "#/components/responses/NotFound" }, - "type": { - "type": "string", - "enum": [ - "checkup", - "vaccination", - "surgery", - "treatment", - "other" - ] + "500": { + "$ref": "#/components/responses/InternalServerError" + } + } + } + }, + "/auth/oauth/{provider}": { + "post": { + "tags": ["Authentication"], + "summary": "OAuth login/register via social provider", + "description": "Authenticates or registers a user via a social OAuth provider (Google, Apple, Facebook). The client obtains an authorization code from the provider and passes it here.", + "operationId": "oauthLogin", + "parameters": [ + { + "name": "provider", + "in": "path", + "required": true, + "schema": { + "type": "string", + "enum": ["google", "apple", "facebook"] + }, + "example": "google" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OAuthCallbackRequest" + } + } + } + }, + "responses": { + "200": { + "description": "OAuth login successful", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LoginResponse" + } + } + } }, - "diagnosis": { - "type": "string" + "400": { + "$ref": "#/components/responses/BadRequest" }, - "treatment": { - "type": "string" + "401": { + "$ref": "#/components/responses/Unauthorized" }, - "notes": { - "type": "string" + "500": { + "$ref": "#/components/responses/InternalServerError" + } + } + } + }, + "/users": { + "get": { + "tags": ["Users"], + "summary": "List all users", + "description": "Returns a paginated list of users. Requires admin role.", + "operationId": "listUsers", + "security": [ + { + "BearerAuth": [] + } + ], + "parameters": [ + { + "$ref": "#/components/parameters/PageParam" }, - "visitDate": { - "type": "string", - "format": "date" + { + "$ref": "#/components/parameters/LimitParam" }, - "nextVisitDate": { - "type": "string", - "format": "date" + { + "$ref": "#/components/parameters/SortByParam" }, - "blockchainTxHash": { - "type": "string", - "description": "Stellar transaction hash" + { + "$ref": "#/components/parameters/SortOrderParam" }, - "blockchainHash": { - "type": "string", - "description": "Hash stored on-chain" + { + "name": "role", + "in": "query", + "schema": { + "type": "string", + "enum": ["owner", "vet", "admin"] + }, + "description": "Filter by user role" }, - "isBlockchainVerified": { - "type": "boolean" + { + "name": "search", + "in": "query", + "schema": { + "type": "string" + }, + "description": "Search by name or email (case-insensitive)", + "example": "jane" + } + ], + "responses": { + "200": { + "description": "Paginated list of users", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/PaginatedResponse" + }, + { + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/User" + } + } + } + } + ] + } + } + } }, - "blockchainVerifiedAt": { - "type": "string", - "format": "date-time" + "401": { + "$ref": "#/components/responses/Unauthorized" }, - "createdAt": { - "type": "string", - "format": "date-time" + "403": { + "$ref": "#/components/responses/Forbidden" }, - "updatedAt": { - "type": "string", - "format": "date-time" + "500": { + "$ref": "#/components/responses/InternalServerError" } - }, - "required": [ - "id", - "petId", - "vetId", - "type", - "visitDate" - ] - }, - "Appointment": { - "type": "object", - "properties": { - "id": { - "type": "string", - "format": "uuid" - }, - "petId": { - "type": "string", - "format": "uuid" - }, - "vetId": { - "type": "string", - "format": "uuid" + } + } + }, + "/users/me": { + "get": { + "tags": ["Users"], + "summary": "Get current authenticated user", + "description": "Returns the profile of the currently authenticated user.", + "operationId": "getCurrentUser", + "security": [ + { + "BearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Current user profile", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/ApiResponse" + }, + { + "properties": { + "data": { + "$ref": "#/components/schemas/User" + } + } + } + ] + } + } + } }, - "date": { - "type": "string", - "format": "date", - "example": "2026-05-10" + "401": { + "$ref": "#/components/responses/Unauthorized" }, - "time": { - "type": "string", - "example": "10:00", - "description": "24-hour HH:MM format" - }, - "durationMinutes": { - "type": "integer", - "example": 30, - "default": 30 - }, - "type": { - "type": "string", - "enum": [ - "ROUTINE_CHECKUP", - "VACCINATION", - "SURGERY", - "DENTAL", - "GROOMING", - "EMERGENCY", - "FOLLOW_UP", - "DIAGNOSTIC", - "SPECIALIST_REFERRAL", - "NUTRITION_CONSULTATION" - ] - }, - "status": { - "type": "string", - "enum": [ - "PENDING", - "CONFIRMED", - "CANCELLED", - "COMPLETED", - "NO_SHOW", - "RESCHEDULED" - ] - }, - "notes": { - "type": "string" + "500": { + "$ref": "#/components/responses/InternalServerError" + } + } + } + }, + "/users/{id}": { + "get": { + "tags": ["Users"], + "summary": "Get user by ID", + "description": "Returns a user profile by ID. Admins can access any user; regular users can only access their own profile.", + "operationId": "getUserById", + "security": [ + { + "BearerAuth": [] + } + ], + "parameters": [ + { + "$ref": "#/components/parameters/UserIdParam" + } + ], + "responses": { + "200": { + "description": "User profile", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/ApiResponse" + }, + { + "properties": { + "data": { + "$ref": "#/components/schemas/User" + } + } + } + ] + } + } + } }, - "cancelledAt": { - "type": "string", - "format": "date-time" + "401": { + "$ref": "#/components/responses/Unauthorized" }, - "cancellationReason": { - "type": "string" + "403": { + "$ref": "#/components/responses/Forbidden" }, - "createdAt": { - "type": "string", - "format": "date-time" + "404": { + "$ref": "#/components/responses/NotFound" }, - "updatedAt": { - "type": "string", - "format": "date-time" + "500": { + "$ref": "#/components/responses/InternalServerError" } - }, - "required": [ - "id", - "petId", - "vetId", - "date", - "time", - "type", - "status" - ] + } }, - "Medication": { - "type": "object", - "properties": { - "id": { - "type": "string", - "format": "uuid" - }, - "petId": { - "type": "string", - "format": "uuid" - }, - "name": { - "type": "string", - "example": "Amoxicillin" - }, - "dosage": { - "type": "string", - "example": "250mg" - }, - "frequency": { - "type": "string", - "example": "once_daily" - }, - "startDate": { - "type": "string", - "format": "date" - }, - "endDate": { - "type": "string", - "format": "date" - }, - "active": { - "type": "boolean", - "default": true + "put": { + "tags": ["Users"], + "summary": "Update user profile", + "description": "Updates editable user profile fields. Admins can update any user; regular users can only update their own profile.", + "operationId": "updateUser", + "security": [ + { + "BearerAuth": [] + } + ], + "parameters": [ + { + "$ref": "#/components/parameters/UserIdParam" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateUserRequest" + }, + "example": { + "name": "Jane Smith", + "phone": "+14155552671" + } + } } }, - "required": [ - "id", - "petId", - "name", - "dosage", - "frequency", - "startDate" - ] - }, - "Payment": { - "type": "object", - "properties": { - "id": { - "type": "string", - "format": "uuid" - }, - "userId": { - "type": "string", - "format": "uuid" - }, - "amount": { - "type": "number", - "example": 9.99 - }, - "currency": { - "type": "string", - "example": "USD" - }, - "status": { - "type": "string", - "enum": [ - "pending", - "completed", - "failed", - "refunded", - "cancelled" - ] + "responses": { + "200": { + "description": "User updated successfully", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/ApiResponse" + }, + { + "properties": { + "data": { + "$ref": "#/components/schemas/UpdateUserResponse" + } + } + } + ] + } + } + } }, - "provider": { - "type": "string", - "enum": [ - "stripe", - "apple_iap", - "google_play", - "stub" - ] + "400": { + "$ref": "#/components/responses/BadRequest" }, - "plan": { - "type": "string", - "enum": [ - "free", - "premium_monthly", - "premium_annual" - ] + "401": { + "$ref": "#/components/responses/Unauthorized" }, - "providerTransactionId": { - "type": "string" + "403": { + "$ref": "#/components/responses/Forbidden" }, - "createdAt": { - "type": "string", - "format": "date-time" + "404": { + "$ref": "#/components/responses/NotFound" }, - "updatedAt": { - "type": "string", - "format": "date-time" + "500": { + "$ref": "#/components/responses/InternalServerError" } } }, - "Subscription": { - "type": "object", - "properties": { - "id": { - "type": "string", - "format": "uuid" - }, - "userId": { - "type": "string", - "format": "uuid" - }, - "plan": { - "type": "string", - "enum": [ - "free", - "premium_monthly", - "premium_annual" - ] - }, - "status": { - "type": "string", - "enum": [ - "active", - "inactive", - "cancelled", - "past_due", - "trialing" - ] - }, - "currentPeriodStart": { - "type": "string", - "format": "date-time" - }, - "currentPeriodEnd": { - "type": "string", - "format": "date-time" + "delete": { + "tags": ["Users"], + "summary": "Delete user account", + "description": "Permanently deletes a user account. Requires admin role.", + "operationId": "deleteUser", + "security": [ + { + "BearerAuth": [] + } + ], + "parameters": [ + { + "$ref": "#/components/parameters/UserIdParam" + } + ], + "responses": { + "204": { + "description": "User deleted successfully (no content)" }, - "cancelAtPeriodEnd": { - "type": "boolean" + "401": { + "$ref": "#/components/responses/Unauthorized" }, - "provider": { - "type": "string", - "enum": [ - "stripe", - "apple_iap", - "google_play", - "stub" - ] + "403": { + "$ref": "#/components/responses/Forbidden" }, - "createdAt": { - "type": "string", - "format": "date-time" + "404": { + "$ref": "#/components/responses/NotFound" }, - "updatedAt": { - "type": "string", - "format": "date-time" + "500": { + "$ref": "#/components/responses/InternalServerError" } } - }, - "CommunityPost": { - "type": "object", - "properties": { - "id": { - "type": "string", - "format": "uuid" - }, - "authorId": { - "type": "string", - "format": "uuid" - }, - "authorName": { - "type": "string" + } + }, + "/pets": { + "get": { + "tags": ["Pets"], + "summary": "List pets", + "description": "Returns a paginated list of pets. Owners see only their own pets; vets and admins can see all pets.", + "operationId": "listPets", + "security": [ + { + "BearerAuth": [] + } + ], + "parameters": [ + { + "$ref": "#/components/parameters/PageParam" }, - "category": { - "type": "string", - "enum": [ - "forum", - "tip", - "app" - ] + { + "$ref": "#/components/parameters/LimitParam" }, - "title": { - "type": "string" + { + "$ref": "#/components/parameters/SortByParam" }, - "body": { - "type": "string" + { + "$ref": "#/components/parameters/SortOrderParam" }, - "petId": { - "type": "string" - }, - "petName": { - "type": "string" - }, - "likes": { - "type": "integer", - "default": 0 - }, - "createdAt": { - "type": "string", - "format": "date-time" - }, - "updatedAt": { - "type": "string", - "format": "date-time" - } - }, - "required": [ - "id", - "authorId", - "category", - "title", - "body" - ] - }, - "AuditLog": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "actorId": { - "type": "string" - }, - "action": { - "type": "string", - "example": "medical_record.created" - }, - "resourceType": { - "type": "string", - "example": "medical_record" - }, - "resourceId": { - "type": "string" - }, - "meta": { - "type": "object" - }, - "createdAt": { - "type": "string", - "format": "date-time" - } - } - } - }, - "responses": { - "Unauthorized": { - "description": "Missing or invalid authentication token", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ApiError" - }, - "example": { - "success": false, - "error": { - "code": "UNAUTHORIZED", - "message": "Authentication required. Please provide a Bearer token." - }, - "timestamp": "2026-04-29T00:00:00.000Z" - } - } - } - }, - "Forbidden": { - "description": "Insufficient permissions", - "content": { - "application/json": { + { + "name": "ownerId", + "in": "query", "schema": { - "$ref": "#/components/schemas/ApiError" + "type": "string", + "format": "uuid" }, - "example": { - "success": false, - "error": { - "code": "FORBIDDEN", - "message": "Access denied." - }, - "timestamp": "2026-04-29T00:00:00.000Z" - } - } - } - }, - "NotFound": { - "description": "Resource not found", - "content": { - "application/json": { + "description": "Filter pets by owner ID", + "example": "a1b2c3d4-e5f6-7890-abcd-ef1234567890" + }, + { + "name": "species", + "in": "query", "schema": { - "$ref": "#/components/schemas/ApiError" + "type": "string", + "enum": ["dog", "cat", "bird", "rabbit", "other"] }, - "example": { - "success": false, - "error": { - "code": "NOT_FOUND", - "message": "Resource not found" - }, - "timestamp": "2026-04-29T00:00:00.000Z" - } - } - } - }, - "ValidationError": { - "description": "Request validation failed", - "content": { - "application/json": { + "description": "Filter by species" + }, + { + "name": "search", + "in": "query", "schema": { - "$ref": "#/components/schemas/ApiError" + "type": "string" }, - "example": { - "success": false, - "error": { - "code": "VALIDATION_ERROR", - "message": "name, species, and ownerId are required" - }, - "timestamp": "2026-04-29T00:00:00.000Z" - } + "description": "Search by pet name (case-insensitive)", + "example": "buddy" } - } - } - } - }, - "security": [ - { - "BearerAuth": [] - } - ], - "paths": { - "/health": { - "get": { - "tags": [ - "Health" ], - "summary": "Service health check", - "security": [], "responses": { "200": { - "description": "Service is healthy", + "description": "Paginated list of pets", "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "ok": { - "type": "boolean" - }, - "service": { - "type": "string" + "allOf": [ + { + "$ref": "#/components/schemas/PaginatedResponse" }, - "timestamp": { - "type": "string", - "format": "date-time" + { + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Pet" + } + } + } } - } - }, - "example": { - "ok": true, - "service": "petchain-api", - "timestamp": "2026-04-29T00:00:00.000Z" + ] } } } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "500": { + "$ref": "#/components/responses/InternalServerError" } } - } - }, - "/users/me": { - "get": { - "tags": [ - "Users" + }, + "post": { + "tags": ["Pets"], + "summary": "Create a new pet", + "description": "Registers a new pet. The `ownerId` must match the authenticated user's ID (or the user must be an admin).", + "operationId": "createPet", + "security": [ + { + "BearerAuth": [] + } ], - "summary": "Get current authenticated user", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreatePetRequest" + }, + "example": { + "name": "Buddy", + "species": "dog", + "breed": "Golden Retriever", + "dateOfBirth": "2021-06-15", + "microchipId": "985141002512345", + "ownerId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890" + } + } + } + }, "responses": { - "200": { - "description": "Current user profile", + "201": { + "description": "Pet created successfully", "content": { "application/json": { "schema": { @@ -850,10 +848,9 @@ "$ref": "#/components/schemas/ApiResponse" }, { - "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/User" + "$ref": "#/components/schemas/Pet" } } } @@ -862,84 +859,55 @@ } } }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, "401": { "$ref": "#/components/responses/Unauthorized" }, - "404": { - "$ref": "#/components/responses/NotFound" + "403": { + "$ref": "#/components/responses/Forbidden" + }, + "500": { + "$ref": "#/components/responses/InternalServerError" } } } }, - "/users": { + "/pets/{id}": { "get": { - "tags": [ - "Users" + "tags": ["Pets"], + "summary": "Get pet by ID", + "description": "Returns full pet details including owner summary.", + "operationId": "getPetById", + "security": [ + { + "BearerAuth": [] + } ], - "summary": "List all users (Admin only)", "parameters": [ { - "name": "page", - "in": "query", - "schema": { - "type": "integer", - "default": 1 - } - }, - { - "name": "limit", - "in": "query", - "schema": { - "type": "integer", - "default": 20, - "maximum": 100 - } - }, - { - "name": "role", - "in": "query", - "schema": { - "type": "string", - "enum": [ - "owner", - "vet", - "admin" - ] - } - }, - { - "name": "search", - "in": "query", - "schema": { - "type": "string" - }, - "description": "Search by name, email, or ID" + "$ref": "#/components/parameters/PetIdParam" } ], "responses": { "200": { - "description": "Paginated list of users", + "description": "Pet details", "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean" + "allOf": [ + { + "$ref": "#/components/schemas/ApiResponse" }, - "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/User" + { + "properties": { + "data": { + "$ref": "#/components/schemas/Pet" + } } - }, - "pagination": { - "$ref": "#/components/schemas/PaginationMeta" - }, - "timestamp": { - "type": "string" } - } + ] } } } @@ -949,62 +917,47 @@ }, "403": { "$ref": "#/components/responses/Forbidden" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "500": { + "$ref": "#/components/responses/InternalServerError" } } }, - "post": { - "tags": [ - "Users" + "put": { + "tags": ["Pets"], + "summary": "Update pet details", + "description": "Updates editable pet fields. Only the pet owner or an admin can update a pet.", + "operationId": "updatePet", + "security": [ + { + "BearerAuth": [] + } + ], + "parameters": [ + { + "$ref": "#/components/parameters/PetIdParam" + } ], - "summary": "Create a new user", - "security": [], "requestBody": { "required": true, "content": { "application/json": { "schema": { - "type": "object", - "required": [ - "email", - "name" - ], - "properties": { - "email": { - "type": "string", - "format": "email", - "example": "jane@example.com" - }, - "name": { - "type": "string", - "example": "Jane Doe" - }, - "phone": { - "type": "string", - "example": "+1234567890" - }, - "role": { - "type": "string", - "enum": [ - "owner", - "vet", - "admin" - ], - "default": "owner" - } - } + "$ref": "#/components/schemas/UpdatePetRequest" }, "example": { - "email": "jane@example.com", - "name": "Jane Doe", - "phone": "+1234567890", - "role": "owner" + "name": "Buddy Jr.", + "breed": "Labrador Retriever" } } } }, "responses": { - "201": { - "description": "User created", + "200": { + "description": "Pet updated successfully", "content": { "application/json": { "schema": { @@ -1013,10 +966,9 @@ "$ref": "#/components/schemas/ApiResponse" }, { - "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/User" + "$ref": "#/components/schemas/Pet" } } } @@ -1026,41 +978,82 @@ } }, "400": { - "$ref": "#/components/responses/ValidationError" + "$ref": "#/components/responses/BadRequest" }, - "409": { - "description": "Email already registered", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ApiError" - } - } - } - } + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "403": { + "$ref": "#/components/responses/Forbidden" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "500": { + "$ref": "#/components/responses/InternalServerError" + } + } + }, + "delete": { + "tags": ["Pets"], + "summary": "Delete a pet", + "description": "Permanently deletes a pet and all associated records. Only the pet owner or an admin can delete a pet.", + "operationId": "deletePet", + "security": [ + { + "BearerAuth": [] + } + ], + "parameters": [ + { + "$ref": "#/components/parameters/PetIdParam" + } + ], + "responses": { + "204": { + "description": "Pet deleted successfully (no content)" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "403": { + "$ref": "#/components/responses/Forbidden" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "500": { + "$ref": "#/components/responses/InternalServerError" + } } } }, - "/users/{id}": { + "/pets/qr/{qrCode}": { "get": { - "tags": [ - "Users" + "tags": ["Pets"], + "summary": "Look up pet by QR code", + "description": "Retrieves a pet profile by scanning its QR code. Used by the QRScannerScreen. The QR code value is a URL-encoded string containing the pet ID and a CryptoJS checksum for tamper detection.", + "operationId": "getPetByQRCode", + "security": [ + { + "BearerAuth": [] + } ], - "summary": "Get user by ID", "parameters": [ { - "name": "id", + "name": "qrCode", "in": "path", "required": true, "schema": { - "type": "string", - "format": "uuid" - } + "type": "string" + }, + "description": "URL-encoded QR code value from the pet's QR tag", + "example": "petchain%3A%2F%2Fpet%2Fa1b2c3d4-e5f6-7890-abcd-ef1234567890" } ], "responses": { "200": { - "description": "User found", + "description": "Pet found", "content": { "application/json": { "schema": { @@ -1069,10 +1062,9 @@ "$ref": "#/components/schemas/ApiResponse" }, { - "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/User" + "$ref": "#/components/schemas/Pet" } } } @@ -1081,64 +1073,63 @@ } } }, + "400": { + "description": "Invalid or tampered QR code", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + }, + "example": { + "success": false, + "error": { + "code": "INVALID_QR_CODE", + "message": "QR code checksum validation failed" + }, + "timestamp": "2024-01-15T10:30:00Z" + } + } + } + }, "401": { "$ref": "#/components/responses/Unauthorized" }, "404": { "$ref": "#/components/responses/NotFound" + }, + "500": { + "$ref": "#/components/responses/InternalServerError" } } - }, - "put": { - "tags": [ - "Users" + } + }, + "/pets/owner/{ownerId}": { + "get": { + "tags": ["Pets"], + "summary": "List pets by owner", + "description": "Returns all pets belonging to a specific owner.", + "operationId": "getPetsByOwner", + "security": [ + { + "BearerAuth": [] + } ], - "summary": "Update user (Admin or self)", "parameters": [ { - "name": "id", + "name": "ownerId", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" - } + }, + "description": "Owner user ID", + "example": "a1b2c3d4-e5f6-7890-abcd-ef1234567890" } ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "phone": { - "type": "string" - }, - "role": { - "type": "string", - "enum": [ - "owner", - "vet", - "admin" - ], - "description": "Admin only" - }, - "isEmailVerified": { - "type": "boolean", - "description": "Admin only" - } - } - } - } - } - }, "responses": { "200": { - "description": "User updated", + "description": "List of pets for the owner", "content": { "application/json": { "schema": { @@ -1147,10 +1138,12 @@ "$ref": "#/components/schemas/ApiResponse" }, { - "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/User" + "type": "array", + "items": { + "$ref": "#/components/schemas/Pet" + } } } } @@ -1167,120 +1160,101 @@ }, "404": { "$ref": "#/components/responses/NotFound" - } - } - }, - "delete": { - "tags": [ - "Users" - ], - "summary": "Delete user (Admin only)", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - } - ], - "responses": { - "200": { - "description": "User deleted", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ApiResponse" - } - } - } - }, - "401": { - "$ref": "#/components/responses/Unauthorized" }, - "403": { - "$ref": "#/components/responses/Forbidden" - }, - "404": { - "$ref": "#/components/responses/NotFound" + "500": { + "$ref": "#/components/responses/InternalServerError" } } } }, - "/pets": { + "/medical-records": { "get": { - "tags": [ - "Pets" + "tags": ["Medical Records"], + "summary": "List medical records", + "description": "Returns a paginated list of medical records with optional filters.", + "operationId": "listMedicalRecords", + "security": [ + { + "BearerAuth": [] + } ], - "summary": "List pets", - "description": "OWNER must supply their own ownerId. VET and ADMIN can list all.", "parameters": [ { - "name": "ownerId", + "$ref": "#/components/parameters/PageParam" + }, + { + "$ref": "#/components/parameters/LimitParam" + }, + { + "$ref": "#/components/parameters/SortByParam" + }, + { + "$ref": "#/components/parameters/SortOrderParam" + }, + { + "name": "petId", "in": "query", "schema": { "type": "string", "format": "uuid" }, - "description": "Required for OWNER role" + "description": "Filter by pet ID" }, { - "name": "species", + "name": "vetId", "in": "query", "schema": { "type": "string", - "enum": [ - "dog", - "cat", - "bird", - "rabbit", - "other" - ] - } + "format": "uuid" + }, + "description": "Filter by veterinarian ID" }, { - "name": "search", + "name": "type", "in": "query", "schema": { - "type": "string" - } + "type": "string", + "enum": ["checkup", "vaccination", "surgery", "treatment", "other"] + }, + "description": "Filter by record type" }, { - "name": "page", + "name": "startDate", "in": "query", "schema": { - "type": "integer", - "default": 1 - } + "type": "string", + "format": "date" + }, + "description": "Filter records from this date (inclusive)", + "example": "2024-01-01" }, { - "name": "limit", + "name": "endDate", "in": "query", "schema": { - "type": "integer", - "default": 20 - } + "type": "string", + "format": "date" + }, + "description": "Filter records up to this date (inclusive)", + "example": "2024-12-31" } ], "responses": { "200": { - "description": "List of pets", + "description": "Paginated list of medical records", "content": { "application/json": { "schema": { "allOf": [ { - "$ref": "#/components/schemas/ApiResponse" + "$ref": "#/components/schemas/PaginatedResponse" }, { - "type": "object", "properties": { "data": { "type": "array", "items": { - "$ref": "#/components/schemas/Pet" + "$ref": "#/components/schemas/MedicalRecord" } } } @@ -1293,82 +1267,54 @@ "401": { "$ref": "#/components/responses/Unauthorized" }, - "403": { - "$ref": "#/components/responses/Forbidden" + "500": { + "$ref": "#/components/responses/InternalServerError" } } }, "post": { - "tags": [ - "Pets" + "tags": ["Medical Records"], + "summary": "Create a medical record", + "description": "Creates a new medical record for a pet. After creation, the record hash is automatically anchored on the Stellar blockchain asynchronously. The `isBlockchainVerified` field will be `false` until anchoring completes.", + "operationId": "createMedicalRecord", + "security": [ + { + "BearerAuth": [] + } ], - "summary": "Create a new pet", - "description": "OWNER can only create pets for themselves. ADMIN can create for any owner.", "requestBody": { "required": true, "content": { "application/json": { "schema": { - "type": "object", - "required": [ - "name", - "species", - "ownerId" - ], - "properties": { - "name": { - "type": "string", - "example": "Buddy" - }, - "species": { - "type": "string", - "enum": [ - "dog", - "cat", - "bird", - "rabbit", - "other" - ] - }, - "breed": { - "type": "string", - "example": "Labrador" - }, - "dateOfBirth": { - "type": "string", - "format": "date", - "example": "2020-01-15" - }, - "microchipId": { - "type": "string" - }, - "photoUrl": { - "type": "string", - "format": "uri" - }, - "thumbnailUrl": { - "type": "string", - "format": "uri" - }, - "ownerId": { - "type": "string", - "format": "uuid" - } - } + "$ref": "#/components/schemas/CreateMedicalRecordRequest" }, "example": { - "name": "Buddy", - "species": "dog", - "breed": "Labrador", - "dateOfBirth": "2020-01-15", - "ownerId": "u-demo-1" + "petId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "vetId": "b2c3d4e5-f6a7-8901-bcde-f12345678901", + "type": "vaccination", + "diagnosis": "Healthy — routine annual checkup", + "treatment": "Administered DHPP booster vaccine", + "notes": "Pet is in excellent health. Weight stable at 28kg.", + "visitDate": "2024-03-15", + "nextVisitDate": "2025-03-15", + "vaccinations": [ + { + "vaccineName": "DHPP", + "administeredAt": "2024-03-15", + "nextDueDate": "2025-03-15", + "manufacturer": "Merck Animal Health", + "batchNumber": "LOT-2024-001", + "dose": "1ml" + } + ] } } } }, "responses": { "201": { - "description": "Pet created", + "description": "Medical record created successfully", "content": { "application/json": { "schema": { @@ -1377,10 +1323,9 @@ "$ref": "#/components/schemas/ApiResponse" }, { - "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/Pet" + "$ref": "#/components/schemas/MedicalRecord" } } } @@ -1390,37 +1335,49 @@ } }, "400": { - "$ref": "#/components/responses/ValidationError" + "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" + }, + "404": { + "description": "Pet or vet not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + } + }, + "500": { + "$ref": "#/components/responses/InternalServerError" } } } }, - "/pets/owner/{ownerId}": { + "/medical-records/{id}": { "get": { - "tags": [ - "Pets" + "tags": ["Medical Records"], + "summary": "Get medical record by ID", + "description": "Returns a single medical record with full details including blockchain verification status.", + "operationId": "getMedicalRecordById", + "security": [ + { + "BearerAuth": [] + } ], - "summary": "Get all pets for a specific owner", "parameters": [ { - "name": "ownerId", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } + "$ref": "#/components/parameters/RecordIdParam" } ], "responses": { "200": { - "description": "Pets for owner", + "description": "Medical record details", "content": { "application/json": { "schema": { @@ -1429,13 +1386,9 @@ "$ref": "#/components/schemas/ApiResponse" }, { - "type": "object", "properties": { "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Pet" - } + "$ref": "#/components/schemas/MedicalRecord" } } } @@ -1449,33 +1402,43 @@ }, "403": { "$ref": "#/components/responses/Forbidden" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "500": { + "$ref": "#/components/responses/InternalServerError" } } - } - }, - "/pets/qr/{qrCode}": { - "get": { - "tags": [ - "Pets" + }, + "put": { + "tags": ["Medical Records"], + "summary": "Update a medical record", + "description": "Updates a medical record. Note: blockchain-verified records (`isBlockchainVerified: true`) cannot be modified — any update attempt will return 409.", + "operationId": "updateMedicalRecord", + "security": [ + { + "BearerAuth": [] + } ], - "summary": "Lookup pet by QR code", - "description": "Public endpoint — no authentication required. Accepts raw pet ID or petchain QR URL.", - "security": [], "parameters": [ { - "name": "qrCode", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "URL-encoded QR code value or pet ID", - "example": "p-demo-1" + "$ref": "#/components/parameters/RecordIdParam" } ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateMedicalRecordRequest" + } + } + } + }, "responses": { "200": { - "description": "Pet found", + "description": "Medical record updated successfully", "content": { "application/json": { "schema": { @@ -1484,10 +1447,9 @@ "$ref": "#/components/schemas/ApiResponse" }, { - "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/Pet" + "$ref": "#/components/schemas/MedicalRecord" } } } @@ -1496,51 +1458,59 @@ } } }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "403": { + "$ref": "#/components/responses/Forbidden" + }, "404": { "$ref": "#/components/responses/NotFound" + }, + "409": { + "description": "Record is blockchain-verified and cannot be modified", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + }, + "example": { + "success": false, + "error": { + "code": "RECORD_IMMUTABLE", + "message": "This record has been anchored on the blockchain and cannot be modified" + }, + "timestamp": "2024-01-15T10:30:00Z" + } + } + } + }, + "500": { + "$ref": "#/components/responses/InternalServerError" } } - } - }, - "/pets/{id}": { - "get": { - "tags": [ - "Pets" + }, + "delete": { + "tags": ["Medical Records"], + "summary": "Delete a medical record", + "description": "Deletes a medical record. Blockchain-verified records cannot be deleted.", + "operationId": "deleteMedicalRecord", + "security": [ + { + "BearerAuth": [] + } ], - "summary": "Get pet by ID", "parameters": [ { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } + "$ref": "#/components/parameters/RecordIdParam" } ], "responses": { - "200": { - "description": "Pet details", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ApiResponse" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/components/schemas/Pet" - } - } - } - ] - } - } - } + "204": { + "description": "Medical record deleted successfully (no content)" }, "401": { "$ref": "#/components/responses/Unauthorized" @@ -1550,81 +1520,88 @@ }, "404": { "$ref": "#/components/responses/NotFound" + }, + "409": { + "description": "Record is blockchain-verified and cannot be deleted", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + } + }, + "500": { + "$ref": "#/components/responses/InternalServerError" } } - }, - "put": { - "tags": [ - "Pets" + } + }, + "/medical-records/pet/{petId}": { + "get": { + "tags": ["Medical Records"], + "summary": "Get all medical records for a pet", + "description": "Returns all medical records for a specific pet, ordered by visit date descending.", + "operationId": "getMedicalRecordsByPet", + "security": [ + { + "BearerAuth": [] + } ], - "summary": "Update pet (Owner or Admin)", "parameters": [ { - "name": "id", - "in": "path", - "required": true, + "$ref": "#/components/parameters/PetIdParam" + }, + { + "name": "type", + "in": "query", "schema": { "type": "string", - "format": "uuid" + "enum": ["checkup", "vaccination", "surgery", "treatment", "other"] } + }, + { + "name": "startDate", + "in": "query", + "schema": { + "type": "string", + "format": "date" + }, + "example": "2024-01-01" + }, + { + "name": "endDate", + "in": "query", + "schema": { + "type": "string", + "format": "date" + }, + "example": "2024-12-31" + }, + { + "$ref": "#/components/parameters/PageParam" + }, + { + "$ref": "#/components/parameters/LimitParam" } ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "species": { - "type": "string" - }, - "breed": { - "type": "string" - }, - "dateOfBirth": { - "type": "string", - "format": "date" - }, - "microchipId": { - "type": "string" - }, - "photoUrl": { - "type": "string", - "format": "uri" - }, - "thumbnailUrl": { - "type": "string", - "format": "uri" - }, - "ownerId": { - "type": "string", - "format": "uuid", - "description": "Admin only — transfer ownership" - } - } - } - } - } - }, "responses": { "200": { - "description": "Pet updated", + "description": "Medical records for the pet", "content": { "application/json": { "schema": { "allOf": [ { - "$ref": "#/components/schemas/ApiResponse" + "$ref": "#/components/schemas/PaginatedResponse" }, { - "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/Pet" + "type": "array", + "items": { + "$ref": "#/components/schemas/MedicalRecord" + } } } } @@ -1641,124 +1618,80 @@ }, "404": { "$ref": "#/components/responses/NotFound" - } - } - }, - "delete": { - "tags": [ - "Pets" - ], - "summary": "Delete pet (Owner or Admin)", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - } - ], - "responses": { - "200": { - "description": "Pet deleted", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ApiResponse" - } - } - } - }, - "401": { - "$ref": "#/components/responses/Unauthorized" - }, - "403": { - "$ref": "#/components/responses/Forbidden" }, - "404": { - "$ref": "#/components/responses/NotFound" + "500": { + "$ref": "#/components/responses/InternalServerError" } } } }, "/pets/{petId}/medical-records": { "get": { - "tags": [ - "Pets", - "Medical Records" - ], + "tags": ["Medical Records"], "summary": "Get medical records for a pet (nested route)", + "description": "Alias for GET /medical-records/pet/{petId}. Supports the same query parameters.", + "operationId": "getPetMedicalRecords", + "security": [ + { + "BearerAuth": [] + } + ], "parameters": [ { - "name": "petId", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } + "$ref": "#/components/parameters/PetIdParam" }, { "name": "type", "in": "query", "schema": { "type": "string", - "enum": [ - "checkup", - "vaccination", - "surgery", - "treatment", - "other" - ] + "enum": ["checkup", "vaccination", "surgery", "treatment", "other"] } }, { - "name": "page", + "name": "startDate", "in": "query", "schema": { - "type": "integer", - "default": 1 + "type": "string", + "format": "date" } }, { - "name": "limit", + "name": "endDate", "in": "query", "schema": { - "type": "integer", - "default": 20, - "maximum": 100 + "type": "string", + "format": "date" } + }, + { + "$ref": "#/components/parameters/PageParam" + }, + { + "$ref": "#/components/parameters/LimitParam" } ], "responses": { "200": { - "description": "Paginated medical records for pet", + "description": "Medical records for the pet", "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/MedicalRecord" - } - }, - "total": { - "type": "integer" - }, - "page": { - "type": "integer" - }, - "limit": { - "type": "integer" + "allOf": [ + { + "$ref": "#/components/schemas/PaginatedResponse" }, - "totalPages": { - "type": "integer" + { + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MedicalRecord" + } + } + } } - } + ] } } } @@ -1771,17 +1704,24 @@ }, "404": { "$ref": "#/components/responses/NotFound" + }, + "500": { + "$ref": "#/components/responses/InternalServerError" } } } }, - "/medical-records": { + "/appointments": { "get": { - "tags": [ - "Medical Records" + "tags": ["Appointments"], + "summary": "List appointments", + "description": "Returns appointments. Owners see their pets' appointments; vets see their own appointments; admins see all.", + "operationId": "listAppointments", + "security": [ + { + "BearerAuth": [] + } ], - "summary": "List medical records", - "description": "OWNER must supply petId for one of their pets.", "parameters": [ { "name": "petId", @@ -1790,7 +1730,7 @@ "type": "string", "format": "uuid" }, - "description": "Required for OWNER role" + "description": "Filter by pet ID" }, { "name": "vetId", @@ -1798,7 +1738,17 @@ "schema": { "type": "string", "format": "uuid" - } + }, + "description": "Filter by vet ID" + }, + { + "name": "status", + "in": "query", + "schema": { + "type": "string", + "enum": ["PENDING", "CONFIRMED", "CANCELLED", "COMPLETED", "NO_SHOW", "RESCHEDULED"] + }, + "description": "Filter by appointment status" }, { "name": "type", @@ -1806,61 +1756,53 @@ "schema": { "type": "string", "enum": [ - "checkup", - "vaccination", - "surgery", - "treatment", - "other" + "ROUTINE_CHECKUP", + "VACCINATION", + "SURGERY", + "DENTAL", + "GROOMING", + "EMERGENCY", + "FOLLOW_UP", + "DIAGNOSTIC", + "SPECIALIST_REFERRAL", + "NUTRITION_CONSULTATION" ] } }, { - "name": "startDate", + "name": "fromDate", "in": "query", "schema": { "type": "string", "format": "date" - } + }, + "description": "Filter appointments from this date (inclusive)", + "example": "2024-03-01" }, { - "name": "endDate", + "name": "toDate", "in": "query", "schema": { "type": "string", "format": "date" - } + }, + "description": "Filter appointments up to this date (inclusive)", + "example": "2024-03-31" }, { - "name": "diagnosis", - "in": "query", - "schema": { - "type": "string" - }, - "description": "Partial match on diagnosis text" + "$ref": "#/components/parameters/PageParam" + }, + { + "$ref": "#/components/parameters/LimitParam" } ], "responses": { "200": { - "description": "List of medical records", + "description": "List of appointments", "content": { "application/json": { "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ApiResponse" - }, - { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/MedicalRecord" - } - } - } - } - ] + "$ref": "#/components/schemas/AppointmentListResponse" } } } @@ -1868,204 +1810,142 @@ "401": { "$ref": "#/components/responses/Unauthorized" }, - "403": { - "$ref": "#/components/responses/Forbidden" + "500": { + "$ref": "#/components/responses/InternalServerError" } } }, "post": { - "tags": [ - "Medical Records" + "tags": ["Appointments"], + "summary": "Create an appointment", + "description": "Books a new appointment for a pet with a veterinarian.", + "operationId": "createAppointment", + "security": [ + { + "BearerAuth": [] + } ], - "summary": "Create medical record (Vet or Admin)", "requestBody": { "required": true, "content": { "application/json": { "schema": { - "type": "object", - "required": [ - "petId", - "vetId", - "type", - "visitDate" - ], - "properties": { - "petId": { - "type": "string", - "format": "uuid" - }, - "vetId": { - "type": "string", - "format": "uuid" - }, - "type": { - "type": "string", - "enum": [ - "checkup", - "vaccination", - "surgery", - "treatment", - "other" - ] - }, - "diagnosis": { - "type": "string" - }, - "treatment": { - "type": "string" - }, - "notes": { - "type": "string" - }, - "visitDate": { - "type": "string", - "format": "date", - "example": "2026-04-29" - }, - "nextVisitDate": { - "type": "string", - "format": "date" - } - } + "$ref": "#/components/schemas/CreateAppointmentRequest" }, "example": { - "petId": "p-demo-1", - "vetId": "v-demo-1", - "type": "vaccination", - "diagnosis": "Annual wellness", - "treatment": "Rabies vaccine", - "visitDate": "2026-04-29" + "petId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "vetId": "b2c3d4e5-f6a7-8901-bcde-f12345678901", + "date": "2024-03-15", + "time": "14:30", + "durationMinutes": 30, + "type": "VACCINATION", + "status": "PENDING", + "notes": "Annual booster vaccines due", + "reminder": { + "isEnabled": true, + "minutesBefore": 60, + "notificationMethod": "push" + } } } } }, "responses": { "201": { - "description": "Medical record created", + "description": "Appointment created successfully", "content": { "application/json": { "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ApiResponse" + "type": "object", + "properties": { + "success": { + "type": "boolean", + "example": true }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/components/schemas/MedicalRecord" - } - } + "data": { + "$ref": "#/components/schemas/Appointment" + }, + "message": { + "type": "string", + "example": "Appointment booked successfully" } - ] + } } } } }, "400": { - "$ref": "#/components/responses/ValidationError" + "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" }, - "403": { - "$ref": "#/components/responses/Forbidden" - } - } - } - }, - "/medical-records/pet/{petId}": { - "get": { - "tags": [ - "Medical Records" - ], - "summary": "Get all medical records for a pet", - "parameters": [ - { - "name": "petId", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - } - ], - "responses": { - "200": { - "description": "Medical records for pet", + "404": { + "description": "Pet or vet not found", "content": { "application/json": { "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ApiResponse" - }, - { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/MedicalRecord" - } - } - } - } - ] + "$ref": "#/components/schemas/ApiError" } } } }, - "401": { - "$ref": "#/components/responses/Unauthorized" - }, - "403": { - "$ref": "#/components/responses/Forbidden" + "409": { + "description": "Time slot already booked", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + }, + "example": { + "success": false, + "error": { + "code": "SLOT_UNAVAILABLE", + "message": "The requested time slot is already booked" + }, + "timestamp": "2024-01-15T10:30:00Z" + } + } + } }, - "404": { - "$ref": "#/components/responses/NotFound" + "500": { + "$ref": "#/components/responses/InternalServerError" } } } }, - "/medical-records/{id}": { + "/appointments/{id}": { "get": { - "tags": [ - "Medical Records" + "tags": ["Appointments"], + "summary": "Get appointment by ID", + "description": "Returns full appointment details including vet and pet summaries.", + "operationId": "getAppointmentById", + "security": [ + { + "BearerAuth": [] + } ], - "summary": "Get medical record by ID", "parameters": [ { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } + "$ref": "#/components/parameters/AppointmentIdParam" } ], "responses": { "200": { - "description": "Medical record", + "description": "Appointment details", "content": { "application/json": { "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ApiResponse" + "type": "object", + "properties": { + "success": { + "type": "boolean", + "example": true }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/components/schemas/MedicalRecord" - } - } + "data": { + "$ref": "#/components/schemas/Appointment" } - ] + } } } } @@ -2078,23 +1958,25 @@ }, "404": { "$ref": "#/components/responses/NotFound" + }, + "500": { + "$ref": "#/components/responses/InternalServerError" } } }, "put": { - "tags": [ - "Medical Records" + "tags": ["Appointments"], + "summary": "Update an appointment", + "description": "Updates appointment details. To cancel, set `status` to `CANCELLED` and provide `cancellationReason`.", + "operationId": "updateAppointment", + "security": [ + { + "BearerAuth": [] + } ], - "summary": "Update medical record (Vet or Admin)", "parameters": [ { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } + "$ref": "#/components/parameters/AppointmentIdParam" } ], "requestBody": { @@ -2102,47 +1984,28 @@ "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "type": { - "type": "string", - "enum": [ - "checkup", - "vaccination", - "surgery", - "treatment", - "other" - ] - }, - "diagnosis": { - "type": "string" - }, - "treatment": { - "type": "string" - }, - "notes": { - "type": "string" - }, - "visitDate": { - "type": "string", - "format": "date" - }, - "nextVisitDate": { - "type": "string", - "format": "date" - }, - "blockchainTxHash": { - "type": "string" - }, - "blockchainHash": { - "type": "string" - }, - "isBlockchainVerified": { - "type": "boolean" - }, - "blockchainVerifiedAt": { - "type": "string", - "format": "date-time" + "$ref": "#/components/schemas/UpdateAppointmentRequest" + }, + "examples": { + "reschedule": { + "summary": "Reschedule appointment", + "value": { + "date": "2024-03-20", + "time": "10:00", + "status": "RESCHEDULED" + } + }, + "cancel": { + "summary": "Cancel appointment", + "value": { + "status": "CANCELLED", + "cancellationReason": "Owner unavailable" + } + }, + "confirm": { + "summary": "Confirm appointment", + "value": { + "status": "CONFIRMED" } } } @@ -2151,64 +2014,26 @@ }, "responses": { "200": { - "description": "Medical record updated", + "description": "Appointment updated successfully", "content": { "application/json": { "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ApiResponse" + "type": "object", + "properties": { + "success": { + "type": "boolean", + "example": true }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/components/schemas/MedicalRecord" - } - } + "data": { + "$ref": "#/components/schemas/Appointment" } - ] + } } } } }, - "401": { - "$ref": "#/components/responses/Unauthorized" - }, - "403": { - "$ref": "#/components/responses/Forbidden" - }, - "404": { - "$ref": "#/components/responses/NotFound" - } - } - }, - "delete": { - "tags": [ - "Medical Records" - ], - "summary": "Delete medical record (Vet or Admin)", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - } - ], - "responses": { - "200": { - "description": "Medical record deleted", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ApiResponse" - } - } - } + "400": { + "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" @@ -2218,59 +2043,77 @@ }, "404": { "$ref": "#/components/responses/NotFound" + }, + "500": { + "$ref": "#/components/responses/InternalServerError" } } } }, - "/appointments": { + "/medications": { "get": { - "tags": [ - "Appointments" + "tags": ["Medications"], + "summary": "List medications", + "description": "Returns medications for a pet. The `petId` query parameter is required.", + "operationId": "listMedications", + "security": [ + { + "BearerAuth": [] + } ], - "summary": "List appointments", - "description": "OWNER must supply petId. VET defaults to their own appointments.", "parameters": [ { "name": "petId", "in": "query", + "required": true, "schema": { "type": "string", "format": "uuid" - } + }, + "description": "Pet ID to fetch medications for", + "example": "a1b2c3d4-e5f6-7890-abcd-ef1234567890" }, { - "name": "vetId", + "name": "status", "in": "query", "schema": { "type": "string", - "format": "uuid" - } + "enum": ["active", "paused", "completed", "discontinued"] + }, + "description": "Filter by medication status" } ], "responses": { "200": { - "description": "List of appointments", + "description": "List of medications", "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean" + "allOf": [ + { + "$ref": "#/components/schemas/ApiResponse" }, - "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Appointment" + { + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Medication" + } + } } - }, - "total": { - "type": "integer" - }, - "timestamp": { - "type": "string" } - } + ] + } + } + } + }, + "400": { + "description": "Missing required petId parameter", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" } } } @@ -2278,167 +2121,162 @@ "401": { "$ref": "#/components/responses/Unauthorized" }, - "403": { - "$ref": "#/components/responses/Forbidden" + "500": { + "$ref": "#/components/responses/InternalServerError" } } }, "post": { - "tags": [ - "Appointments" + "tags": ["Medications"], + "summary": "Create a medication schedule", + "description": "Creates a new medication schedule for a pet.", + "operationId": "createMedication", + "security": [ + { + "BearerAuth": [] + } ], - "summary": "Create appointment", "requestBody": { "required": true, "content": { "application/json": { "schema": { - "type": "object", - "required": [ - "petId", - "vetId", - "date", - "time" - ], - "properties": { - "petId": { - "type": "string", - "format": "uuid" - }, - "vetId": { - "type": "string", - "format": "uuid" - }, - "date": { - "type": "string", - "format": "date", - "example": "2026-05-10" - }, - "time": { - "type": "string", - "example": "10:00", - "description": "24-hour HH:MM" - }, - "durationMinutes": { - "type": "integer", - "default": 30 - }, - "type": { - "type": "string", - "enum": [ - "ROUTINE_CHECKUP", - "VACCINATION", - "SURGERY", - "DENTAL", - "GROOMING", - "EMERGENCY", - "FOLLOW_UP", - "DIAGNOSTIC", - "SPECIALIST_REFERRAL", - "NUTRITION_CONSULTATION" - ], - "default": "ROUTINE_CHECKUP" - }, - "status": { - "type": "string", - "enum": [ - "PENDING", - "CONFIRMED", - "CANCELLED", - "COMPLETED", - "NO_SHOW", - "RESCHEDULED" - ], - "default": "PENDING" - }, - "notes": { - "type": "string" - } - } + "$ref": "#/components/schemas/CreateMedicationRequest" }, "example": { - "petId": "p-demo-1", - "vetId": "v-demo-1", - "date": "2026-05-10", - "time": "10:00", - "type": "ROUTINE_CHECKUP" + "petId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "name": "Apoquel", + "dosage": "16mg", + "frequency": "once_daily", + "durationDays": 30, + "startDate": "2024-03-15", + "endDate": "2024-04-14", + "status": "active", + "instructions": "Give with food. Monitor for side effects." } } } }, "responses": { "201": { - "description": "Appointment created", + "description": "Medication schedule created", "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean" - }, - "data": { - "$ref": "#/components/schemas/Appointment" + "allOf": [ + { + "$ref": "#/components/schemas/ApiResponse" }, - "timestamp": { - "type": "string" - } - } - } - } - } + { + "properties": { + "data": { + "$ref": "#/components/schemas/Medication" + } + } + } + ] + } + } + } }, "400": { - "$ref": "#/components/responses/ValidationError" + "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" + }, + "404": { + "description": "Pet not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + } + }, + "500": { + "$ref": "#/components/responses/InternalServerError" } } } }, - "/appointments/{id}": { - "get": { - "tags": [ - "Appointments" + "/medications/{id}": { + "put": { + "tags": ["Medications"], + "summary": "Update a medication", + "description": "Updates a medication schedule. Use `status: \"discontinued\"` to stop a medication early.", + "operationId": "updateMedication", + "security": [ + { + "BearerAuth": [] + } ], - "summary": "Get appointment by ID", "parameters": [ { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } + "$ref": "#/components/parameters/MedicationIdParam" } ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateMedicationRequest" + }, + "examples": { + "pause": { + "summary": "Pause medication", + "value": { + "status": "paused" + } + }, + "discontinue": { + "summary": "Discontinue medication", + "value": { + "status": "discontinued", + "endDate": "2024-03-20" + } + }, + "updateDosage": { + "summary": "Update dosage", + "value": { + "dosage": "8mg", + "instructions": "Reduced dose — give with food" + } + } + } + } + } + }, "responses": { "200": { - "description": "Appointment details", + "description": "Medication updated successfully", "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean" - }, - "data": { - "$ref": "#/components/schemas/Appointment" + "allOf": [ + { + "$ref": "#/components/schemas/ApiResponse" }, - "timestamp": { - "type": "string" + { + "properties": { + "data": { + "$ref": "#/components/schemas/Medication" + } + } } - } + ] } } } }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, "401": { "$ref": "#/components/responses/Unauthorized" }, @@ -2447,23 +2285,55 @@ }, "404": { "$ref": "#/components/responses/NotFound" + }, + "500": { + "$ref": "#/components/responses/InternalServerError" } } }, - "put": { - "tags": [ - "Appointments" + "delete": { + "tags": ["Medications"], + "summary": "Delete a medication", + "description": "Permanently deletes a medication record.", + "operationId": "deleteMedication", + "security": [ + { + "BearerAuth": [] + } ], - "summary": "Update appointment (Owner, assigned Vet, or Admin)", "parameters": [ { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } + "$ref": "#/components/parameters/MedicationIdParam" + } + ], + "responses": { + "204": { + "description": "Medication deleted successfully (no content)" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "403": { + "$ref": "#/components/responses/Forbidden" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "500": { + "$ref": "#/components/responses/InternalServerError" + } + } + } + }, + "/blockchain/records/store": { + "post": { + "tags": ["Blockchain"], + "summary": "Anchor a record hash on Stellar", + "description": "Stores a SHA-256 hash of a medical record on the Stellar blockchain. The hash is embedded in a Stellar transaction memo field. Returns the transaction details once confirmed.", + "operationId": "storeRecordOnChain", + "security": [ + { + "BearerAuth": [] } ], "requestBody": { @@ -2471,149 +2341,112 @@ "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "date": { - "type": "string", - "format": "date" - }, - "time": { - "type": "string" - }, - "durationMinutes": { - "type": "integer" - }, - "type": { - "type": "string", - "enum": [ - "ROUTINE_CHECKUP", - "VACCINATION", - "SURGERY", - "DENTAL", - "GROOMING", - "EMERGENCY", - "FOLLOW_UP", - "DIAGNOSTIC", - "SPECIALIST_REFERRAL", - "NUTRITION_CONSULTATION" - ] - }, - "status": { - "type": "string", - "enum": [ - "PENDING", - "CONFIRMED", - "CANCELLED", - "COMPLETED", - "NO_SHOW", - "RESCHEDULED" - ] - }, - "notes": { - "type": "string" - }, - "cancellationReason": { - "type": "string", - "description": "Required when status is CANCELLED" - } + "$ref": "#/components/schemas/StoreRecordRequest" + }, + "example": { + "recordId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "hash": "a3f5c8d2e1b4f7a9c2d5e8f1b4c7d0e3f6a9b2c5d8e1f4a7b0c3d6e9f2a5b8", + "metadata": { + "petId": "b2c3d4e5-f6a7-8901-bcde-f12345678901", + "recordType": "vaccination" } } } } }, "responses": { - "200": { - "description": "Appointment updated", + "201": { + "description": "Hash anchored on Stellar successfully", "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean" - }, - "data": { - "$ref": "#/components/schemas/Appointment" + "allOf": [ + { + "$ref": "#/components/schemas/ApiResponse" }, - "timestamp": { - "type": "string" + { + "properties": { + "data": { + "$ref": "#/components/schemas/StellarTransactionDetails" + } + } } - } + ] } } } }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, "401": { "$ref": "#/components/responses/Unauthorized" }, - "403": { - "$ref": "#/components/responses/Forbidden" - }, - "404": { - "$ref": "#/components/responses/NotFound" - } - } - }, - "delete": { - "tags": [ - "Appointments" - ], - "summary": "Delete appointment (Owner or Admin)", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - } - ], - "responses": { - "200": { - "description": "Appointment deleted", + "408": { + "description": "Blockchain request timed out", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ApiResponse" + "$ref": "#/components/schemas/ApiError" } } } }, - "401": { - "$ref": "#/components/responses/Unauthorized" + "429": { + "$ref": "#/components/responses/TooManyRequests" }, - "403": { - "$ref": "#/components/responses/Forbidden" + "500": { + "$ref": "#/components/responses/InternalServerError" }, - "404": { - "$ref": "#/components/responses/NotFound" + "503": { + "description": "Stellar network temporarily unavailable", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + }, + "example": { + "success": false, + "error": { + "code": "SERVICE_UNAVAILABLE", + "message": "Blockchain service is temporarily unavailable" + }, + "timestamp": "2024-01-15T10:30:00Z" + } + } + } } } } }, - "/medications": { - "get": { - "tags": [ - "Medications" - ], - "summary": "List medications", - "description": "OWNER must supply petId for one of their pets.", - "parameters": [ + "/blockchain/records/verify": { + "post": { + "tags": ["Blockchain"], + "summary": "Verify a record hash against Stellar", + "description": "Verifies that a given SHA-256 hash matches the hash anchored on the Stellar blockchain for the specified record. Returns `verified: true` if the hashes match (record is untampered).", + "operationId": "verifyRecordOnChain", + "security": [ { - "name": "petId", - "in": "query", - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Required for OWNER role" + "BearerAuth": [] } ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/VerifyRecordRequest" + }, + "example": { + "recordId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "hash": "a3f5c8d2e1b4f7a9c2d5e8f1b4c7d0e3f6a9b2c5d8e1f4a7b0c3d6e9f2a5b8" + } + } + } + }, "responses": { "200": { - "description": "List of medications", + "description": "Verification result", "content": { "application/json": { "schema": { @@ -2622,92 +2455,104 @@ "$ref": "#/components/schemas/ApiResponse" }, { - "type": "object", "properties": { "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Medication" - } + "$ref": "#/components/schemas/StellarRecordVerification" } } } ] + }, + "examples": { + "verified": { + "summary": "Record is authentic", + "value": { + "success": true, + "data": { + "verified": true, + "recordId": "a1b2c3d4-...", + "onChainHash": "a3f5c8d2...", + "txHash": "stellar-tx-...", + "ledger": 48234567 + }, + "timestamp": "2024-01-15T10:30:00Z" + } + }, + "tampered": { + "summary": "Record has been tampered with", + "value": { + "success": true, + "data": { + "verified": false, + "recordId": "a1b2c3d4-...", + "onChainHash": "a3f5c8d2...", + "txHash": "stellar-tx-..." + }, + "timestamp": "2024-01-15T10:30:00Z" + } + } } } } }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, "401": { "$ref": "#/components/responses/Unauthorized" }, - "403": { - "$ref": "#/components/responses/Forbidden" + "404": { + "description": "Record not found on blockchain", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + } + }, + "500": { + "$ref": "#/components/responses/InternalServerError" } } - }, + } + }, + "/blockchain/records/batch-verify": { "post": { - "tags": [ - "Medications" + "tags": ["Blockchain"], + "summary": "Batch verify multiple record hashes", + "description": "Verifies up to 50 record hashes in a single request. Useful for bulk integrity checks.", + "operationId": "batchVerifyRecords", + "security": [ + { + "BearerAuth": [] + } ], - "summary": "Create medication (Vet or Admin)", "requestBody": { "required": true, "content": { "application/json": { "schema": { - "type": "object", - "required": [ - "petId", - "name", - "dosage", - "frequency", - "startDate" - ], - "properties": { - "petId": { - "type": "string", - "format": "uuid" - }, - "name": { - "type": "string", - "example": "Amoxicillin" - }, - "dosage": { - "type": "string", - "example": "250mg" - }, - "frequency": { - "type": "string", - "example": "once_daily", - "description": "once_daily | twice_daily | three_times_daily | every_other_day | weekly | as_needed" - }, - "startDate": { - "type": "string", - "format": "date" - }, - "endDate": { - "type": "string", - "format": "date" - }, - "active": { - "type": "boolean", - "default": true - } - } + "$ref": "#/components/schemas/BatchVerifyRequest" }, "example": { - "petId": "p-demo-1", - "name": "Amoxicillin", - "dosage": "250mg", - "frequency": "twice_daily", - "startDate": "2026-04-29" + "records": [ + { + "recordId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "hash": "a3f5c8d2..." + }, + { + "recordId": "b2c3d4e5-f6a7-8901-bcde-f12345678901", + "hash": "b4e6d0f2..." + } + ] } } } }, "responses": { - "201": { - "description": "Medication created", + "200": { + "description": "Batch verification results", "content": { "application/json": { "schema": { @@ -2716,10 +2561,12 @@ "$ref": "#/components/schemas/ApiResponse" }, { - "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/Medication" + "type": "array", + "items": { + "$ref": "#/components/schemas/StellarRecordVerification" + } } } } @@ -2729,23 +2576,31 @@ } }, "400": { - "$ref": "#/components/responses/ValidationError" + "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" }, - "403": { - "$ref": "#/components/responses/Forbidden" + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/InternalServerError" } } } }, - "/medications/{id}": { + "/blockchain/records/{id}/hash": { "get": { - "tags": [ - "Medications" + "tags": ["Blockchain"], + "summary": "Retrieve anchored hash for a record", + "description": "Fetches the hash and transaction details that were anchored on Stellar for a specific record.", + "operationId": "retrieveRecordHash", + "security": [ + { + "BearerAuth": [] + } ], - "summary": "Get medication by ID", "parameters": [ { "name": "id", @@ -2754,12 +2609,14 @@ "schema": { "type": "string", "format": "uuid" - } + }, + "description": "Medical record ID", + "example": "a1b2c3d4-e5f6-7890-abcd-ef1234567890" } ], "responses": { "200": { - "description": "Medication details", + "description": "Anchored hash details", "content": { "application/json": { "schema": { @@ -2768,10 +2625,9 @@ "$ref": "#/components/schemas/ApiResponse" }, { - "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/Medication" + "$ref": "#/components/schemas/RetrieveRecordHashResponse" } } } @@ -2783,69 +2639,41 @@ "401": { "$ref": "#/components/responses/Unauthorized" }, - "403": { - "$ref": "#/components/responses/Forbidden" - }, "404": { "$ref": "#/components/responses/NotFound" + }, + "500": { + "$ref": "#/components/responses/InternalServerError" } } - }, - "put": { - "tags": [ - "Medications" + } + }, + "/blockchain/transactions/{txHash}": { + "get": { + "tags": ["Blockchain"], + "summary": "Get Stellar transaction details", + "description": "Fetches full details of a Stellar transaction by its hash.", + "operationId": "getTransactionDetails", + "security": [ + { + "BearerAuth": [] + } ], - "summary": "Update medication (Vet or Admin)", "parameters": [ { - "name": "id", + "name": "txHash", "in": "path", "required": true, "schema": { - "type": "string", - "format": "uuid" - } + "type": "string" + }, + "description": "Stellar transaction hash (64-char hex)", + "example": "a3f5c8d2e1b4f7a9c2d5e8f1b4c7d0e3f6a9b2c5d8e1f4a7b0c3d6e9f2a5b8" } ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "dosage": { - "type": "string" - }, - "frequency": { - "type": "string" - }, - "startDate": { - "type": "string", - "format": "date" - }, - "endDate": { - "type": "string", - "format": "date" - }, - "active": { - "type": "boolean" - }, - "petId": { - "type": "string", - "format": "uuid" - } - } - } - } - } - }, "responses": { "200": { - "description": "Medication updated", + "description": "Transaction details", "content": { "application/json": { "schema": { @@ -2854,10 +2682,9 @@ "$ref": "#/components/schemas/ApiResponse" }, { - "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/Medication" + "$ref": "#/components/schemas/StellarTransactionDetails" } } } @@ -2869,62 +2696,59 @@ "401": { "$ref": "#/components/responses/Unauthorized" }, - "403": { - "$ref": "#/components/responses/Forbidden" - }, "404": { "$ref": "#/components/responses/NotFound" + }, + "500": { + "$ref": "#/components/responses/InternalServerError" } } - }, - "delete": { - "tags": [ - "Medications" + } + }, + "/blockchain/transactions/history": { + "get": { + "tags": ["Blockchain"], + "summary": "Get transaction history", + "description": "Returns Stellar transaction history, optionally filtered by record or account.", + "operationId": "getTransactionHistory", + "security": [ + { + "BearerAuth": [] + } ], - "summary": "Delete medication (Vet or Admin)", "parameters": [ { - "name": "id", - "in": "path", - "required": true, + "name": "recordId", + "in": "query", "schema": { "type": "string", "format": "uuid" - } - } - ], - "responses": { - "200": { - "description": "Medication deleted", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ApiResponse" - } - } - } - }, - "401": { - "$ref": "#/components/responses/Unauthorized" + }, + "description": "Filter by medical record ID" }, - "403": { - "$ref": "#/components/responses/Forbidden" + { + "name": "accountId", + "in": "query", + "schema": { + "type": "string" + }, + "description": "Filter by Stellar account ID" }, - "404": { - "$ref": "#/components/responses/NotFound" + { + "name": "limit", + "in": "query", + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 200, + "default": 50 + }, + "description": "Maximum number of transactions to return" } - } - } - }, - "/payments/plans": { - "get": { - "tags": [ - "Payments" ], - "summary": "List subscription plans", "responses": { "200": { - "description": "Available plans", + "description": "Transaction history", "content": { "application/json": { "schema": { @@ -2933,43 +2757,11 @@ "$ref": "#/components/schemas/ApiResponse" }, { - "type": "object", "properties": { "data": { "type": "array", "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "enum": [ - "free", - "premium_monthly", - "premium_annual" - ] - }, - "name": { - "type": "string" - }, - "description": { - "type": "string" - }, - "priceMonthly": { - "type": "number" - }, - "priceAnnual": { - "type": "number" - }, - "currency": { - "type": "string" - }, - "features": { - "type": "array", - "items": { - "type": "string" - } - } - } + "$ref": "#/components/schemas/StellarTransactionDetails" } } } @@ -2981,19 +2773,27 @@ }, "401": { "$ref": "#/components/responses/Unauthorized" + }, + "500": { + "$ref": "#/components/responses/InternalServerError" } } } }, - "/payments/subscription": { + "/blockchain/network/info": { "get": { - "tags": [ - "Payments" + "tags": ["Blockchain"], + "summary": "Get Stellar network info", + "description": "Returns current Stellar network status including the active ledger number.", + "operationId": "getStellarNetworkInfo", + "security": [ + { + "BearerAuth": [] + } ], - "summary": "Get current user subscription", "responses": { "200": { - "description": "Current subscription", + "description": "Stellar network information", "content": { "application/json": { "schema": { @@ -3002,895 +2802,1903 @@ "$ref": "#/components/schemas/ApiResponse" }, { - "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/Subscription" + "$ref": "#/components/schemas/NetworkInfoResponse" } } } ] + }, + "example": { + "success": true, + "data": { + "network": "mainnet", + "horizonUrl": "https://horizon.stellar.org", + "passphrase": "Public Global Stellar Network ; September 2015", + "currentLedger": 48234567, + "latestLedger": 48234570 + }, + "timestamp": "2024-01-15T10:30:00Z" } } } }, "401": { "$ref": "#/components/responses/Unauthorized" - } - } - }, - "delete": { - "tags": [ - "Payments" - ], - "summary": "Cancel subscription at period end", - "responses": { - "200": { - "description": "Subscription will be cancelled at period end", + }, + "500": { + "$ref": "#/components/responses/InternalServerError" + }, + "503": { + "description": "Stellar network unavailable", "content": { "application/json": { "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ApiResponse" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/components/schemas/Subscription" - } - } - } - ] + "$ref": "#/components/schemas/ApiError" } } } - }, - "401": { - "$ref": "#/components/responses/Unauthorized" - }, - "404": { - "$ref": "#/components/responses/NotFound" } } } + } + }, + "components": { + "securitySchemes": { + "BearerAuth": { + "type": "http", + "scheme": "bearer", + "bearerFormat": "JWT", + "description": "JWT access token obtained from `POST /auth/login` or `POST /auth/register`.\n\n**Example:**\n```\nAuthorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...\n```\n\nTokens expire after 7 days. Use `POST /auth/refresh` to obtain a new token." + }, + "ApiKeyAuth": { + "type": "apiKey", + "in": "header", + "name": "X-API-Key", + "description": "API key for server-to-server integrations. Contact engineering@petchain.app to obtain a key." + } }, - "/payments/initiate": { - "post": { - "tags": [ - "Payments" - ], - "summary": "Initiate a payment (create payment intent)", - "description": "FUTURE FEATURE — payment processing is stubbed.", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "plan", - "provider" - ], - "properties": { - "plan": { - "type": "string", - "enum": [ - "premium_monthly", - "premium_annual" - ] - }, - "provider": { - "type": "string", - "enum": [ - "stripe", - "apple_iap", - "google_play", - "stub" - ] - }, - "providerTransactionId": { - "type": "string" - } - } - }, - "example": { - "plan": "premium_monthly", - "provider": "stub" - } - } - } - }, - "responses": { - "201": { - "description": "Payment initiated", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ApiResponse" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/components/schemas/Payment" - } - } - } - ] - } - } - } + "schemas": { + "ApiResponse": { + "type": "object", + "required": ["success", "data", "timestamp"], + "properties": { + "success": { + "type": "boolean", + "example": true }, - "400": { - "$ref": "#/components/responses/ValidationError" + "data": {}, + "message": { + "type": "string", + "example": "Operation completed successfully" }, - "401": { - "$ref": "#/components/responses/Unauthorized" + "timestamp": { + "type": "string", + "format": "date-time", + "example": "2024-01-15T10:30:00Z" } } - } - }, - "/payments/{paymentId}/confirm": { - "post": { - "tags": [ - "Payments" - ], - "summary": "Confirm payment and activate subscription", - "parameters": [ - { - "name": "paymentId", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - } - ], - "responses": { - "200": { - "description": "Payment confirmed, subscription activated", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ApiResponse" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/components/schemas/Subscription" - } - } - } - ] - } - } - } + }, + "ApiError": { + "type": "object", + "required": ["success", "error", "timestamp"], + "properties": { + "success": { + "type": "boolean", + "example": false }, - "400": { - "description": "Payment already processed or invalid state", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ApiError" + "error": { + "type": "object", + "required": ["code", "message"], + "properties": { + "code": { + "type": "string", + "example": "VALIDATION_ERROR" + }, + "message": { + "type": "string", + "example": "Invalid input data" + }, + "details": { + "type": "object", + "additionalProperties": true, + "example": { + "field": "email", + "issue": "Invalid format" } } } }, - "401": { - "$ref": "#/components/responses/Unauthorized" - }, - "404": { - "$ref": "#/components/responses/NotFound" + "timestamp": { + "type": "string", + "format": "date-time", + "example": "2024-01-15T10:30:00Z" } } - } - }, - "/payments/history": { - "get": { - "tags": [ - "Payments" - ], - "summary": "Get payment history for current user", - "responses": { - "200": { - "description": "Payment history", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ApiResponse" - }, - { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Payment" - } - } - } - } - ] - } + }, + "PaginationMeta": { + "type": "object", + "required": ["page", "limit", "total", "totalPages", "hasNext", "hasPrev"], + "properties": { + "page": { + "type": "integer", + "minimum": 1, + "example": 1 + }, + "limit": { + "type": "integer", + "minimum": 1, + "maximum": 100, + "example": 20 + }, + "total": { + "type": "integer", + "minimum": 0, + "example": 42 + }, + "totalPages": { + "type": "integer", + "minimum": 0, + "example": 3 + }, + "hasNext": { + "type": "boolean", + "example": true + }, + "hasPrev": { + "type": "boolean", + "example": false + } + } + }, + "PaginatedResponse": { + "type": "object", + "required": ["success", "data", "pagination", "timestamp"], + "properties": { + "success": { + "type": "boolean", + "example": true + }, + "data": { + "type": "array", + "items": {} + }, + "pagination": { + "$ref": "#/components/schemas/PaginationMeta" + }, + "timestamp": { + "type": "string", + "format": "date-time", + "example": "2024-01-15T10:30:00Z" + } + } + }, + "UUID": { + "type": "string", + "format": "uuid", + "example": "a1b2c3d4-e5f6-7890-abcd-ef1234567890" + }, + "ISODate": { + "type": "string", + "format": "date", + "example": "2024-01-15" + }, + "ISODateTime": { + "type": "string", + "format": "date-time", + "example": "2024-01-15T10:30:00Z" + }, + "LoginRequest": { + "type": "object", + "required": ["email", "password"], + "properties": { + "email": { + "type": "string", + "format": "email", + "example": "jane.doe@example.com", + "description": "Registered user email address" + }, + "password": { + "type": "string", + "format": "password", + "minLength": 8, + "example": "SecurePass123!", + "description": "User password (min 8 chars, must include upper, lower, number, special)" + } + } + }, + "RegisterRequest": { + "type": "object", + "required": ["email", "name", "password"], + "properties": { + "email": { + "type": "string", + "format": "email", + "example": "jane.doe@example.com" + }, + "name": { + "type": "string", + "minLength": 2, + "maxLength": 100, + "example": "Jane Doe" + }, + "password": { + "type": "string", + "format": "password", + "minLength": 8, + "maxLength": 128, + "example": "SecurePass123!", + "description": "Must include uppercase, lowercase, number, and special character" + }, + "phone": { + "type": "string", + "example": "+14155552671", + "description": "E.164 format phone number (optional)" + }, + "role": { + "type": "string", + "enum": ["owner", "vet", "admin"], + "default": "owner", + "example": "owner" + } + } + }, + "RefreshTokenRequest": { + "type": "object", + "required": ["refreshToken"], + "properties": { + "refreshToken": { + "type": "string", + "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", + "description": "The refresh token issued at login" + } + } + }, + "ForgotPasswordRequest": { + "type": "object", + "required": ["email"], + "properties": { + "email": { + "type": "string", + "format": "email", + "example": "jane.doe@example.com" + } + } + }, + "ResetPasswordRequest": { + "type": "object", + "required": ["token", "newPassword"], + "properties": { + "token": { + "type": "string", + "example": "reset-token-abc123", + "description": "Password reset token received via email" + }, + "newPassword": { + "type": "string", + "format": "password", + "minLength": 8, + "maxLength": 128, + "example": "NewSecurePass456!" + } + } + }, + "VerifyEmailRequest": { + "type": "object", + "required": ["token"], + "properties": { + "token": { + "type": "string", + "example": "verify-token-xyz789", + "description": "Email verification token received via email" + } + } + }, + "OAuthCallbackRequest": { + "type": "object", + "required": ["code"], + "properties": { + "code": { + "type": "string", + "example": "oauth-authorization-code", + "description": "OAuth 2.0 authorization code from the provider" + }, + "redirectUri": { + "type": "string", + "format": "uri", + "example": "petchain://auth/callback" + } + } + }, + "AuthUserSummary": { + "type": "object", + "required": ["id", "email", "name", "role"], + "properties": { + "id": { + "$ref": "#/components/schemas/UUID" + }, + "email": { + "type": "string", + "format": "email", + "example": "jane.doe@example.com" + }, + "name": { + "type": "string", + "example": "Jane Doe" + }, + "role": { + "type": "string", + "enum": ["owner", "vet", "admin"], + "example": "owner" + } + } + }, + "LoginResponse": { + "type": "object", + "required": ["user", "token", "expiresIn"], + "properties": { + "user": { + "$ref": "#/components/schemas/AuthUserSummary" + }, + "token": { + "type": "string", + "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyLWlkIiwiZXhwIjoxNzA5MDAwMDAwfQ.signature", + "description": "JWT access token — include as Authorization: Bearer {token}" + }, + "refreshToken": { + "type": "string", + "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", + "description": "Long-lived refresh token for obtaining new access tokens" + }, + "expiresIn": { + "type": "integer", + "example": 604800, + "description": "Access token lifetime in seconds (default: 7 days)" + } + } + }, + "RegisterResponse": { + "type": "object", + "required": ["user", "token"], + "properties": { + "user": { + "$ref": "#/components/schemas/AuthUserSummary" + }, + "token": { + "type": "string", + "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." + }, + "refreshToken": { + "type": "string", + "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." + } + } + }, + "RefreshTokenResponse": { + "type": "object", + "required": ["token", "expiresIn"], + "properties": { + "token": { + "type": "string", + "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." + }, + "refreshToken": { + "type": "string", + "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." + }, + "expiresIn": { + "type": "integer", + "example": 604800 + } + } + }, + "User": { + "type": "object", + "required": [ + "id", + "email", + "name", + "role", + "pets", + "createdAt", + "updatedAt", + "isEmailVerified", + "authProvider" + ], + "properties": { + "id": { + "$ref": "#/components/schemas/UUID" + }, + "email": { + "type": "string", + "format": "email", + "example": "jane.doe@example.com" + }, + "name": { + "type": "string", + "example": "Jane Doe" + }, + "phone": { + "type": "string", + "example": "+14155552671" + }, + "role": { + "type": "string", + "enum": ["owner", "vet", "admin"], + "example": "owner", + "description": "User role controlling access permissions" + }, + "authProvider": { + "type": "string", + "enum": ["local", "google", "apple"], + "example": "local" + }, + "isEmailVerified": { + "type": "boolean", + "example": true + }, + "lastLoginAt": { + "$ref": "#/components/schemas/ISODateTime" + }, + "pets": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PetReference" + }, + "description": "Lightweight references to pets owned by this user" + }, + "createdAt": { + "$ref": "#/components/schemas/ISODateTime" + }, + "updatedAt": { + "$ref": "#/components/schemas/ISODateTime" + } + } + }, + "PetReference": { + "type": "object", + "required": ["id"], + "properties": { + "id": { + "$ref": "#/components/schemas/UUID" + }, + "name": { + "type": "string", + "example": "Buddy" + } + } + }, + "UpdateUserRequest": { + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 2, + "maxLength": 100, + "example": "Jane Smith" + }, + "phone": { + "type": "string", + "example": "+14155552671" + }, + "role": { + "type": "string", + "enum": ["owner", "vet", "admin"], + "example": "vet" + } + } + }, + "UpdateUserResponse": { + "type": "object", + "required": ["id", "email", "name", "role", "updatedAt"], + "properties": { + "id": { + "$ref": "#/components/schemas/UUID" + }, + "email": { + "type": "string", + "format": "email", + "example": "jane.doe@example.com" + }, + "name": { + "type": "string", + "example": "Jane Smith" + }, + "phone": { + "type": "string", + "example": "+14155552671" + }, + "role": { + "type": "string", + "enum": ["owner", "vet", "admin"], + "example": "vet" + }, + "updatedAt": { + "$ref": "#/components/schemas/ISODateTime" + } + } + }, + "Pet": { + "type": "object", + "required": ["id", "name", "species", "ownerId", "createdAt", "updatedAt"], + "properties": { + "id": { + "$ref": "#/components/schemas/UUID" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100, + "example": "Buddy" + }, + "species": { + "type": "string", + "enum": ["dog", "cat", "bird", "rabbit", "other"], + "example": "dog" + }, + "breed": { + "type": "string", + "example": "Golden Retriever" + }, + "dateOfBirth": { + "$ref": "#/components/schemas/ISODate" + }, + "microchipId": { + "type": "string", + "example": "985141002512345", + "description": "15-digit ISO 11784/11785 microchip identifier" + }, + "photoUrl": { + "type": "string", + "format": "uri", + "example": "https://storage.petchain.app/pets/buddy.jpg" + }, + "ownerId": { + "$ref": "#/components/schemas/UUID" + }, + "owner": { + "type": "object", + "properties": { + "id": { + "$ref": "#/components/schemas/UUID" + }, + "name": { + "type": "string", + "example": "Jane Doe" + }, + "email": { + "type": "string", + "format": "email", + "example": "jane.doe@example.com" + } + } + }, + "createdAt": { + "$ref": "#/components/schemas/ISODateTime" + }, + "updatedAt": { + "$ref": "#/components/schemas/ISODateTime" + } + } + }, + "CreatePetRequest": { + "type": "object", + "required": ["name", "species", "ownerId"], + "properties": { + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100, + "example": "Buddy" + }, + "species": { + "type": "string", + "enum": ["dog", "cat", "bird", "rabbit", "other"], + "example": "dog" + }, + "breed": { + "type": "string", + "example": "Golden Retriever" + }, + "dateOfBirth": { + "$ref": "#/components/schemas/ISODate", + "description": "Pet date of birth in YYYY-MM-DD format" + }, + "microchipId": { + "type": "string", + "example": "985141002512345" + }, + "photoUrl": { + "type": "string", + "format": "uri", + "example": "https://storage.petchain.app/pets/buddy.jpg" + }, + "ownerId": { + "$ref": "#/components/schemas/UUID" + } + } + }, + "UpdatePetRequest": { + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100, + "example": "Buddy Jr." + }, + "species": { + "type": "string", + "enum": ["dog", "cat", "bird", "rabbit", "other"] + }, + "breed": { + "type": "string", + "example": "Labrador Retriever" + }, + "dateOfBirth": { + "$ref": "#/components/schemas/ISODate" + }, + "microchipId": { + "type": "string", + "example": "985141002512345" + }, + "photoUrl": { + "type": "string", + "format": "uri" + } + } + }, + "MedicalRecord": { + "type": "object", + "required": ["id", "petId", "vetId", "type", "visitDate", "createdAt", "updatedAt"], + "properties": { + "id": { + "$ref": "#/components/schemas/UUID" + }, + "petId": { + "$ref": "#/components/schemas/UUID" + }, + "vetId": { + "$ref": "#/components/schemas/UUID" + }, + "type": { + "type": "string", + "enum": ["checkup", "vaccination", "surgery", "treatment", "other"], + "example": "vaccination" + }, + "diagnosis": { + "type": "string", + "example": "Healthy — routine annual checkup" + }, + "treatment": { + "type": "string", + "example": "Administered DHPP booster vaccine" + }, + "notes": { + "type": "string", + "example": "Pet is in excellent health. Weight stable." + }, + "visitDate": { + "$ref": "#/components/schemas/ISODate" + }, + "nextVisitDate": { + "$ref": "#/components/schemas/ISODate" + }, + "diagnosisDetails": { + "$ref": "#/components/schemas/Diagnosis" + }, + "treatmentDetails": { + "$ref": "#/components/schemas/Treatment" + }, + "prescriptions": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Prescription" + } + }, + "vaccinations": { + "type": "array", + "items": { + "$ref": "#/components/schemas/VaccinationRecord" + } + }, + "documents": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MedicalDocument" + } + }, + "veterinarian": { + "$ref": "#/components/schemas/Veterinarian" + }, + "hash": { + "type": "string", + "example": "a3f5c8d2e1b4...", + "description": "SHA-256 hash of the record payload" + }, + "txHash": { + "type": "string", + "example": "stellar-tx-hash-abc123", + "description": "Stellar transaction hash" + }, + "isBlockchainVerified": { + "type": "boolean", + "example": true + }, + "blockchainVerifiedAt": { + "$ref": "#/components/schemas/ISODateTime" + }, + "createdAt": { + "$ref": "#/components/schemas/ISODateTime" + }, + "updatedAt": { + "$ref": "#/components/schemas/ISODateTime" + } + } + }, + "Diagnosis": { + "type": "object", + "required": ["diagnosisText"], + "properties": { + "diagnosisText": { + "type": "string", + "example": "Mild seasonal allergies" + }, + "code": { + "type": "string", + "example": "L30.9", + "description": "ICD-10 or veterinary diagnosis code" + }, + "severity": { + "type": "string", + "enum": ["mild", "moderate", "severe", "unknown"], + "example": "mild" + } + } + }, + "Treatment": { + "type": "object", + "required": ["treatmentText"], + "properties": { + "treatmentText": { + "type": "string", + "example": "Antihistamine prescribed for 2 weeks" + }, + "procedureName": { + "type": "string", + "example": "Allergy skin test" + }, + "outcome": { + "type": "string", + "example": "Resolved — follow-up in 30 days" + } + } + }, + "Prescription": { + "type": "object", + "required": ["medicationName"], + "properties": { + "id": { + "$ref": "#/components/schemas/UUID" + }, + "medicationName": { + "type": "string", + "example": "Apoquel" + }, + "dosage": { + "type": "string", + "example": "16mg" + }, + "route": { + "type": "string", + "example": "oral", + "description": "Administration route (oral, topical, injection)" + }, + "frequency": { + "type": "string", + "example": "once_daily" + }, + "startDate": { + "$ref": "#/components/schemas/ISODate" + }, + "endDate": { + "$ref": "#/components/schemas/ISODate" + }, + "instructions": { + "type": "string", + "example": "Give with food" + } + } + }, + "VaccinationRecord": { + "type": "object", + "required": ["vaccineName"], + "properties": { + "vaccineName": { + "type": "string", + "example": "DHPP" + }, + "administeredAt": { + "$ref": "#/components/schemas/ISODate" + }, + "nextDueDate": { + "$ref": "#/components/schemas/ISODate" + }, + "manufacturer": { + "type": "string", + "example": "Merck Animal Health" + }, + "batchNumber": { + "type": "string", + "example": "LOT-2024-001" + }, + "dose": { + "type": "string", + "example": "1ml" + } + } + }, + "MedicalDocument": { + "type": "object", + "required": ["id", "name", "mimeType", "type", "url"], + "properties": { + "id": { + "$ref": "#/components/schemas/UUID" + }, + "name": { + "type": "string", + "example": "blood-panel-results.pdf" + }, + "mimeType": { + "type": "string", + "example": "application/pdf" + }, + "type": { + "type": "string", + "enum": ["pdf", "image", "other"], + "example": "pdf" + }, + "url": { + "type": "string", + "format": "uri", + "example": "https://storage.petchain.app/docs/blood-panel.pdf" + }, + "sizeBytes": { + "type": "integer", + "example": 204800 + }, + "createdAt": { + "$ref": "#/components/schemas/ISODateTime" + } + } + }, + "Veterinarian": { + "type": "object", + "required": ["id", "name", "email"], + "properties": { + "id": { + "$ref": "#/components/schemas/UUID" + }, + "name": { + "type": "string", + "example": "Dr. Sarah Johnson" + }, + "email": { + "type": "string", + "format": "email", + "example": "dr.johnson@vetclinic.com" + }, + "phone": { + "type": "string", + "example": "+14155559876" + }, + "licenseNumber": { + "type": "string", + "example": "VET-CA-12345" + }, + "clinic": { + "type": "object", + "properties": { + "name": { + "type": "string", + "example": "Downtown Animal Hospital" + }, + "address": { + "type": "string", + "example": "123 Main St, San Francisco, CA 94102" + }, + "phone": { + "type": "string", + "example": "+14155550100" } } + } + } + }, + "CreateMedicalRecordRequest": { + "type": "object", + "required": ["petId", "vetId", "type", "visitDate"], + "properties": { + "petId": { + "$ref": "#/components/schemas/UUID" + }, + "vetId": { + "$ref": "#/components/schemas/UUID" + }, + "type": { + "type": "string", + "enum": ["checkup", "vaccination", "surgery", "treatment", "other"], + "example": "vaccination" + }, + "diagnosis": { + "type": "string", + "example": "Healthy — routine annual checkup" + }, + "treatment": { + "type": "string", + "example": "Administered DHPP booster vaccine" + }, + "notes": { + "type": "string", + "example": "Pet is in excellent health." + }, + "visitDate": { + "$ref": "#/components/schemas/ISODate" + }, + "nextVisitDate": { + "$ref": "#/components/schemas/ISODate" + }, + "diagnosisDetails": { + "$ref": "#/components/schemas/Diagnosis" + }, + "treatmentDetails": { + "$ref": "#/components/schemas/Treatment" + }, + "prescriptions": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Prescription" + } + }, + "vaccinations": { + "type": "array", + "items": { + "$ref": "#/components/schemas/VaccinationRecord" + } + }, + "documents": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MedicalDocument" + } + } + } + }, + "UpdateMedicalRecordRequest": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["checkup", "vaccination", "surgery", "treatment", "other"] + }, + "diagnosis": { + "type": "string" + }, + "treatment": { + "type": "string" + }, + "notes": { + "type": "string" + }, + "visitDate": { + "$ref": "#/components/schemas/ISODate" + }, + "nextVisitDate": { + "$ref": "#/components/schemas/ISODate" + }, + "diagnosisDetails": { + "$ref": "#/components/schemas/Diagnosis" + }, + "treatmentDetails": { + "$ref": "#/components/schemas/Treatment" + }, + "prescriptions": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Prescription" + } + }, + "vaccinations": { + "type": "array", + "items": { + "$ref": "#/components/schemas/VaccinationRecord" + } + }, + "documents": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MedicalDocument" + } + } + } + }, + "Appointment": { + "type": "object", + "required": [ + "id", + "petId", + "vetId", + "date", + "time", + "type", + "status", + "createdAt", + "updatedAt" + ], + "properties": { + "id": { + "$ref": "#/components/schemas/UUID" + }, + "petId": { + "$ref": "#/components/schemas/UUID" + }, + "vetId": { + "$ref": "#/components/schemas/UUID" + }, + "date": { + "type": "string", + "format": "date", + "example": "2024-03-15", + "description": "Appointment date in YYYY-MM-DD format" + }, + "time": { + "type": "string", + "pattern": "^([01]\\d|2[0-3]):[0-5]\\d$", + "example": "14:30", + "description": "24-hour time format HH:MM" + }, + "durationMinutes": { + "type": "integer", + "minimum": 15, + "maximum": 480, + "default": 30, + "example": 30 + }, + "type": { + "type": "string", + "enum": [ + "ROUTINE_CHECKUP", + "VACCINATION", + "SURGERY", + "DENTAL", + "GROOMING", + "EMERGENCY", + "FOLLOW_UP", + "DIAGNOSTIC", + "SPECIALIST_REFERRAL", + "NUTRITION_CONSULTATION" + ], + "example": "VACCINATION" + }, + "status": { + "type": "string", + "enum": ["PENDING", "CONFIRMED", "CANCELLED", "COMPLETED", "NO_SHOW", "RESCHEDULED"], + "example": "CONFIRMED" + }, + "notes": { + "type": "string", + "example": "Annual booster vaccines due" + }, + "vet": { + "$ref": "#/components/schemas/AppointmentVetSummary" + }, + "pet": { + "$ref": "#/components/schemas/AppointmentPetSummary" + }, + "reminder": { + "$ref": "#/components/schemas/AppointmentReminder" + }, + "cancelledAt": { + "$ref": "#/components/schemas/ISODateTime" + }, + "cancellationReason": { + "type": "string", + "example": "Owner unavailable" + }, + "createdAt": { + "$ref": "#/components/schemas/ISODateTime" + }, + "updatedAt": { + "$ref": "#/components/schemas/ISODateTime" + } + } + }, + "AppointmentVetSummary": { + "type": "object", + "required": ["vetId", "name", "clinicName"], + "properties": { + "vetId": { + "$ref": "#/components/schemas/UUID" + }, + "name": { + "type": "string", + "example": "Dr. Sarah Johnson" + }, + "specialization": { + "type": "string", + "example": "General Practice" }, - "401": { - "$ref": "#/components/responses/Unauthorized" + "clinicName": { + "type": "string", + "example": "Downtown Animal Hospital" + }, + "clinicPhone": { + "type": "string", + "example": "+14155550100" + }, + "clinicAddress": { + "type": "string", + "example": "123 Main St, San Francisco, CA 94102" } } - } - }, - "/analytics/events": { - "post": { - "tags": [ - "Analytics" - ], - "summary": "Record an analytics event", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "type", - "name" - ], - "properties": { - "type": { - "type": "string", - "enum": [ - "screen_view", - "feature_usage", - "error" - ] - }, - "name": { - "type": "string", - "example": "PetProfileScreen" - }, - "meta": { - "type": "object", - "additionalProperties": true - }, - "timestamp": { - "type": "integer", - "description": "Unix ms timestamp; defaults to server time" - } - } - }, - "example": { - "type": "screen_view", - "name": "PetProfileScreen", - "meta": { - "petId": "p-demo-1" - } - } - } + }, + "AppointmentPetSummary": { + "type": "object", + "required": ["petId", "name", "species"], + "properties": { + "petId": { + "$ref": "#/components/schemas/UUID" + }, + "name": { + "type": "string", + "example": "Buddy" + }, + "species": { + "type": "string", + "example": "dog" + }, + "breed": { + "type": "string", + "example": "Golden Retriever" + }, + "age": { + "type": "number", + "example": 3.5 } - }, - "responses": { - "201": { - "description": "Event recorded", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ApiResponse" - } - } + } + }, + "AppointmentReminder": { + "type": "object", + "required": ["isEnabled", "minutesBefore", "notificationMethod"], + "properties": { + "isEnabled": { + "type": "boolean", + "example": true + }, + "minutesBefore": { + "type": "integer", + "minimum": 5, + "example": 60, + "description": "Minutes before appointment to send reminder" + }, + "notificationMethod": { + "type": "string", + "enum": ["push", "email", "sms"], + "example": "push" + } + } + }, + "CreateAppointmentRequest": { + "type": "object", + "required": ["petId", "vetId", "date", "time", "type", "status"], + "properties": { + "petId": { + "$ref": "#/components/schemas/UUID" + }, + "vetId": { + "$ref": "#/components/schemas/UUID" + }, + "date": { + "type": "string", + "format": "date", + "example": "2024-03-15" + }, + "time": { + "type": "string", + "pattern": "^([01]\\d|2[0-3]):[0-5]\\d$", + "example": "14:30" + }, + "durationMinutes": { + "type": "integer", + "minimum": 15, + "maximum": 480, + "default": 30, + "example": 30 + }, + "type": { + "type": "string", + "enum": [ + "ROUTINE_CHECKUP", + "VACCINATION", + "SURGERY", + "DENTAL", + "GROOMING", + "EMERGENCY", + "FOLLOW_UP", + "DIAGNOSTIC", + "SPECIALIST_REFERRAL", + "NUTRITION_CONSULTATION" + ], + "example": "VACCINATION" + }, + "status": { + "type": "string", + "enum": ["PENDING", "CONFIRMED", "CANCELLED", "COMPLETED", "NO_SHOW", "RESCHEDULED"], + "default": "PENDING", + "example": "PENDING" + }, + "notes": { + "type": "string", + "example": "Annual booster vaccines due" + }, + "reminder": { + "$ref": "#/components/schemas/AppointmentReminder" + } + } + }, + "UpdateAppointmentRequest": { + "type": "object", + "properties": { + "date": { + "type": "string", + "format": "date", + "example": "2024-03-20" + }, + "time": { + "type": "string", + "pattern": "^([01]\\d|2[0-3]):[0-5]\\d$", + "example": "10:00" + }, + "durationMinutes": { + "type": "integer", + "minimum": 15, + "maximum": 480 + }, + "type": { + "type": "string", + "enum": [ + "ROUTINE_CHECKUP", + "VACCINATION", + "SURGERY", + "DENTAL", + "GROOMING", + "EMERGENCY", + "FOLLOW_UP", + "DIAGNOSTIC", + "SPECIALIST_REFERRAL", + "NUTRITION_CONSULTATION" + ] + }, + "status": { + "type": "string", + "enum": ["PENDING", "CONFIRMED", "CANCELLED", "COMPLETED", "NO_SHOW", "RESCHEDULED"] + }, + "notes": { + "type": "string" + }, + "reminder": { + "$ref": "#/components/schemas/AppointmentReminder" + }, + "cancellationReason": { + "type": "string", + "example": "Owner unavailable" + } + } + }, + "AppointmentListResponse": { + "type": "object", + "required": ["success", "data", "total"], + "properties": { + "success": { + "type": "boolean", + "example": true + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Appointment" } }, - "400": { - "$ref": "#/components/responses/ValidationError" + "total": { + "type": "integer", + "example": 5 }, - "401": { - "$ref": "#/components/responses/Unauthorized" + "page": { + "type": "integer", + "example": 1 + }, + "pageSize": { + "type": "integer", + "example": 20 + }, + "message": { + "type": "string" } } - } - }, - "/analytics/dashboard": { - "get": { - "tags": [ - "Analytics" + }, + "Medication": { + "type": "object", + "required": [ + "id", + "petId", + "name", + "dosage", + "frequency", + "durationDays", + "startDate", + "status", + "createdAt", + "updatedAt" ], - "summary": "View analytics dashboard (Admin only)", - "responses": { - "200": { - "description": "Aggregated analytics", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ApiResponse" - }, - { - "type": "object", - "properties": { - "data": { - "type": "object", - "properties": { - "totalEvents": { - "type": "integer" - }, - "screenViews": { - "type": "object", - "additionalProperties": { - "type": "integer" - } - }, - "featureUsage": { - "type": "object", - "additionalProperties": { - "type": "integer" - } - }, - "errors": { - "type": "object", - "additionalProperties": { - "type": "integer" - } - } - } - } - } - } - ] - } - } - } + "properties": { + "id": { + "$ref": "#/components/schemas/UUID" + }, + "petId": { + "$ref": "#/components/schemas/UUID" + }, + "name": { + "type": "string", + "example": "Apoquel" + }, + "dosage": { + "type": "string", + "example": "16mg" + }, + "frequency": { + "type": "string", + "enum": [ + "once_daily", + "twice_daily", + "three_times_daily", + "every_other_day", + "weekly", + "as_needed" + ], + "example": "once_daily", + "description": "How often the medication should be administered" + }, + "durationDays": { + "type": "integer", + "minimum": 1, + "example": 30, + "description": "Total duration of the medication course in days" + }, + "startDate": { + "$ref": "#/components/schemas/ISODate" + }, + "endDate": { + "$ref": "#/components/schemas/ISODate" + }, + "status": { + "type": "string", + "enum": ["active", "paused", "completed", "discontinued"], + "example": "active" + }, + "instructions": { + "type": "string", + "example": "Give with food. Monitor for side effects." + }, + "createdAt": { + "$ref": "#/components/schemas/ISODateTime" + }, + "updatedAt": { + "$ref": "#/components/schemas/ISODateTime" + } + } + }, + "CreateMedicationRequest": { + "type": "object", + "required": ["petId", "name", "dosage", "frequency", "durationDays", "startDate"], + "properties": { + "petId": { + "$ref": "#/components/schemas/UUID" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 200, + "example": "Apoquel" + }, + "dosage": { + "type": "string", + "example": "16mg" + }, + "frequency": { + "type": "string", + "enum": [ + "once_daily", + "twice_daily", + "three_times_daily", + "every_other_day", + "weekly", + "as_needed" + ], + "example": "once_daily" + }, + "durationDays": { + "type": "integer", + "minimum": 1, + "example": 30 + }, + "startDate": { + "$ref": "#/components/schemas/ISODate" + }, + "endDate": { + "$ref": "#/components/schemas/ISODate" }, - "401": { - "$ref": "#/components/responses/Unauthorized" + "status": { + "type": "string", + "enum": ["active", "paused", "completed", "discontinued"], + "default": "active", + "example": "active" }, - "403": { - "$ref": "#/components/responses/Forbidden" + "instructions": { + "type": "string", + "example": "Give with food" } } - } - }, - "/audit-logs": { - "get": { - "tags": [ - "Audit Logs" - ], - "summary": "Query audit logs (Admin only)", - "parameters": [ - { - "name": "actorId", - "in": "query", - "schema": { - "type": "string" - } + }, + "UpdateMedicationRequest": { + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1, + "maxLength": 200 }, - { - "name": "action", - "in": "query", - "schema": { - "type": "string" - }, - "description": "e.g. medical_record.created, user.deleted" + "dosage": { + "type": "string" }, - { - "name": "resourceType", - "in": "query", - "schema": { - "type": "string" - }, - "description": "e.g. medical_record, user, pet" + "frequency": { + "type": "string", + "enum": [ + "once_daily", + "twice_daily", + "three_times_daily", + "every_other_day", + "weekly", + "as_needed" + ] }, - { - "name": "resourceId", - "in": "query", - "schema": { - "type": "string" - } + "durationDays": { + "type": "integer", + "minimum": 1 }, - { - "name": "startDate", - "in": "query", - "schema": { - "type": "string", - "format": "date-time" - } + "startDate": { + "$ref": "#/components/schemas/ISODate" }, - { - "name": "endDate", - "in": "query", - "schema": { - "type": "string", - "format": "date-time" - } + "endDate": { + "$ref": "#/components/schemas/ISODate" }, - { - "name": "page", - "in": "query", - "schema": { - "type": "integer", - "default": 1 - } + "status": { + "type": "string", + "enum": ["active", "paused", "completed", "discontinued"] }, - { - "name": "limit", - "in": "query", - "schema": { - "type": "integer", - "default": 50, - "maximum": 200 - } + "instructions": { + "type": "string" } - ], - "responses": { - "200": { - "description": "Paginated audit log entries", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean" - }, - "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/AuditLog" - } - }, - "pagination": { - "$ref": "#/components/schemas/PaginationMeta" - }, - "timestamp": { - "type": "string" - } - } - } - } - } + } + }, + "StellarTransactionDetails": { + "type": "object", + "required": ["hash", "successful"], + "properties": { + "hash": { + "type": "string", + "example": "a3f5c8d2e1b4f7a9c2d5e8f1b4c7d0e3f6a9b2c5d8e1f4a7b0c3d6e9f2a5b8", + "description": "Stellar transaction hash (64-char hex)" }, - "401": { - "$ref": "#/components/responses/Unauthorized" + "successful": { + "type": "boolean", + "example": true }, - "403": { - "$ref": "#/components/responses/Forbidden" + "ledger": { + "type": "integer", + "example": 48234567 + }, + "createdAt": { + "$ref": "#/components/schemas/ISODateTime" + }, + "sourceAccount": { + "type": "string", + "example": "GABC...XYZ", + "description": "Stellar account that submitted the transaction" + }, + "feeCharged": { + "type": "string", + "example": "100", + "description": "Fee in stroops (1 XLM = 10,000,000 stroops)" + }, + "memo": { + "type": "string", + "example": "petchain:record:abc123" + }, + "operationCount": { + "type": "integer", + "example": 1 } } - } - }, - "/backups/me": { - "get": { - "tags": [ - "Backups" - ], - "summary": "Get current user backup", - "responses": { - "200": { - "description": "User backup payload", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ApiResponse" - }, - { - "type": "object", - "properties": { - "data": { - "type": "object", - "additionalProperties": true - } - } - } - ] - } - } - } + }, + "StellarRecordVerification": { + "type": "object", + "required": ["verified", "recordId"], + "properties": { + "verified": { + "type": "boolean", + "example": true, + "description": "True if the submitted hash matches the on-chain anchored hash" }, - "401": { - "$ref": "#/components/responses/Unauthorized" + "recordId": { + "$ref": "#/components/schemas/UUID" }, - "404": { - "$ref": "#/components/responses/NotFound" + "onChainHash": { + "type": "string", + "example": "a3f5c8d2e1b4f7a9...", + "description": "The hash stored on the Stellar blockchain" + }, + "txHash": { + "type": "string", + "example": "stellar-tx-hash-abc123" + }, + "ledger": { + "type": "integer", + "example": 48234567 + }, + "timestamp": { + "$ref": "#/components/schemas/ISODateTime" } } }, - "post": { - "tags": [ - "Backups" - ], - "summary": "Save backup for current user", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "description": "Any JSON object representing the user data snapshot" - }, - "example": { - "pets": [], - "appointments": [], - "medications": [] - } - } - } - }, - "responses": { - "201": { - "description": "Backup saved", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ApiResponse" - }, - { - "type": "object", - "properties": { - "data": { - "type": "object", - "additionalProperties": true - } - } - } - ] - } - } - } + "StoreRecordRequest": { + "type": "object", + "required": ["recordId", "hash"], + "properties": { + "recordId": { + "$ref": "#/components/schemas/UUID" }, - "400": { - "$ref": "#/components/responses/ValidationError" + "hash": { + "type": "string", + "pattern": "^[a-f0-9]{64}$", + "example": "a3f5c8d2e1b4f7a9c2d5e8f1b4c7d0e3f6a9b2c5d8e1f4a7b0c3d6e9f2a5b8", + "description": "SHA-256 hex hash of the medical record payload" }, - "401": { - "$ref": "#/components/responses/Unauthorized" + "metadata": { + "type": "object", + "additionalProperties": true, + "example": { + "petId": "uuid", + "vetId": "uuid", + "recordType": "vaccination" + }, + "description": "Optional metadata to anchor alongside the hash" } } - } - }, - "/community/posts": { - "get": { - "tags": [ - "Community" - ], - "summary": "List community posts (newest first)", - "responses": { - "200": { - "description": "List of posts", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ApiResponse" - }, - { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/CommunityPost" - } - } - } - } - ] - } - } - } + }, + "VerifyRecordRequest": { + "type": "object", + "required": ["recordId", "hash"], + "properties": { + "recordId": { + "$ref": "#/components/schemas/UUID" }, - "401": { - "$ref": "#/components/responses/Unauthorized" + "hash": { + "type": "string", + "pattern": "^[a-f0-9]{64}$", + "example": "a3f5c8d2e1b4f7a9c2d5e8f1b4c7d0e3f6a9b2c5d8e1f4a7b0c3d6e9f2a5b8", + "description": "SHA-256 hash to verify against the on-chain record" } } }, - "post": { - "tags": [ - "Community" - ], - "summary": "Create a community post", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "category", - "title", - "body" - ], - "properties": { - "category": { - "type": "string", - "enum": [ - "forum", - "tip", - "app" - ] - }, - "title": { - "type": "string", - "example": "Tips for giving pills to cats" - }, - "body": { - "type": "string", - "example": "Hide the pill in a treat..." - }, - "petId": { - "type": "string", - "format": "uuid" - }, - "petName": { - "type": "string" - } + "RetrieveRecordHashResponse": { + "type": "object", + "required": ["hash", "txHash", "timestamp"], + "properties": { + "hash": { + "type": "string", + "example": "a3f5c8d2e1b4f7a9..." + }, + "txHash": { + "type": "string", + "example": "stellar-tx-hash-abc123" + }, + "timestamp": { + "$ref": "#/components/schemas/ISODateTime" + }, + "ledger": { + "type": "integer", + "example": 48234567 + } + } + }, + "BatchVerifyRequest": { + "type": "object", + "required": ["records"], + "properties": { + "records": { + "type": "array", + "minItems": 1, + "maxItems": 50, + "items": { + "type": "object", + "required": ["recordId", "hash"], + "properties": { + "recordId": { + "$ref": "#/components/schemas/UUID" + }, + "hash": { + "type": "string", + "pattern": "^[a-f0-9]{64}$", + "example": "a3f5c8d2e1b4f7a9..." } - }, - "example": { - "category": "tip", - "title": "Tips for giving pills to cats", - "body": "Hide the pill in a treat or use a pill pocket." } - } + }, + "description": "Array of record ID + hash pairs to verify (max 50)" } - }, - "responses": { - "200": { - "description": "Post created", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ApiResponse" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/components/schemas/CommunityPost" - } - } - } - ] - } - } - } + } + }, + "NetworkInfoResponse": { + "type": "object", + "required": ["network", "horizonUrl", "passphrase", "currentLedger", "latestLedger"], + "properties": { + "network": { + "type": "string", + "enum": ["mainnet", "testnet"], + "example": "mainnet" }, - "400": { - "$ref": "#/components/responses/ValidationError" + "horizonUrl": { + "type": "string", + "format": "uri", + "example": "https://horizon.stellar.org" }, - "401": { - "$ref": "#/components/responses/Unauthorized" + "passphrase": { + "type": "string", + "example": "Public Global Stellar Network ; September 2015" + }, + "currentLedger": { + "type": "integer", + "example": 48234567 + }, + "latestLedger": { + "type": "integer", + "example": 48234570 } } } }, - "/community/posts/{id}/like": { - "post": { - "tags": [ - "Community" - ], - "summary": "Like a community post", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, + "parameters": { + "PageParam": { + "name": "page", + "in": "query", + "schema": { + "type": "integer", + "minimum": 1, + "default": 1 + }, + "description": "Page number for pagination" + }, + "LimitParam": { + "name": "limit", + "in": "query", + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 100, + "default": 20 + }, + "description": "Number of items per page" + }, + "SortByParam": { + "name": "sortBy", + "in": "query", + "schema": { + "type": "string" + }, + "description": "Field to sort by (e.g. \"createdAt\", \"name\")" + }, + "SortOrderParam": { + "name": "sortOrder", + "in": "query", + "schema": { + "type": "string", + "enum": ["asc", "desc"], + "default": "desc" + }, + "description": "Sort direction" + }, + "UserIdParam": { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "User ID (UUID)", + "example": "a1b2c3d4-e5f6-7890-abcd-ef1234567890" + }, + "PetIdParam": { + "name": "petId", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Pet ID (UUID)", + "example": "a1b2c3d4-e5f6-7890-abcd-ef1234567890" + }, + "RecordIdParam": { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Medical record ID (UUID)", + "example": "a1b2c3d4-e5f6-7890-abcd-ef1234567890" + }, + "AppointmentIdParam": { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Appointment ID (UUID)", + "example": "a1b2c3d4-e5f6-7890-abcd-ef1234567890" + }, + "MedicationIdParam": { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Medication ID (UUID)", + "example": "a1b2c3d4-e5f6-7890-abcd-ef1234567890" + } + }, + "responses": { + "BadRequest": { + "description": "Bad request — invalid input or missing required fields", + "content": { + "application/json": { "schema": { - "type": "string", - "format": "uuid" - } - } - ], - "responses": { - "200": { - "description": "Post liked", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ApiResponse" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/components/schemas/CommunityPost" - } - } - } - ] + "$ref": "#/components/schemas/ApiError" + }, + "example": { + "success": false, + "error": { + "code": "VALIDATION_ERROR", + "message": "Invalid input data", + "details": { + "field": "email", + "issue": "Invalid email format" } - } + }, + "timestamp": "2024-01-15T10:30:00Z" } - }, - "401": { - "$ref": "#/components/responses/Unauthorized" - }, - "404": { - "$ref": "#/components/responses/NotFound" } } - } - }, - "/community/posts/{id}": { - "delete": { - "tags": [ - "Community" - ], - "summary": "Delete own community post", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, + }, + "Unauthorized": { + "description": "Unauthorized — missing or invalid JWT token", + "content": { + "application/json": { "schema": { - "type": "string", - "format": "uuid" + "$ref": "#/components/schemas/ApiError" + }, + "example": { + "success": false, + "error": { + "code": "UNAUTHORIZED", + "message": "Authentication required. Please log in." + }, + "timestamp": "2024-01-15T10:30:00Z" } } - ], - "responses": { - "200": { - "description": "Post deleted", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ApiResponse" - }, - { - "type": "object", - "properties": { - "data": { - "type": "object", - "properties": { - "deleted": { - "type": "boolean" - } - } - } - } - } - ] - } - } + } + }, + "Forbidden": { + "description": "Forbidden — authenticated but insufficient permissions", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + }, + "example": { + "success": false, + "error": { + "code": "FORBIDDEN", + "message": "Access denied." + }, + "timestamp": "2024-01-15T10:30:00Z" } - }, - "401": { - "$ref": "#/components/responses/Unauthorized" - }, - "403": { - "$ref": "#/components/responses/Forbidden" - }, - "404": { - "$ref": "#/components/responses/NotFound" } } - } - }, - "/import/pets": { - "post": { - "tags": [ - "Import" - ], - "summary": "Bulk import pets from CSV (Vet or Admin)", - "description": "Upload raw CSV text. Required columns: name, species, ownerId. Optional: breed, dateOfBirth (YYYY-MM-DD), microchipId.", - "requestBody": { - "required": true, - "content": { - "text/plain": { - "schema": { - "type": "string" + }, + "NotFound": { + "description": "Resource not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + }, + "example": { + "success": false, + "error": { + "code": "NOT_FOUND", + "message": "Resource not found." }, - "example": "name,species,ownerId,breed,dateOfBirth\nBuddy,dog,u-demo-1,Labrador,2020-01-15\nWhiskers,cat,u-demo-1,,2019-06-01" + "timestamp": "2024-01-15T10:30:00Z" } } + } + }, + "TooManyRequests": { + "description": "Rate limit exceeded", + "headers": { + "Retry-After": { + "schema": { + "type": "integer" + }, + "description": "Seconds to wait before retrying" + } }, - "responses": { - "200": { - "description": "Import complete (no rows imported)", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ApiResponse" - }, - { - "type": "object", - "properties": { - "data": { - "type": "object", - "properties": { - "imported": { - "type": "integer" - }, - "skipped": { - "type": "integer" - }, - "errors": { - "type": "array", - "items": { - "type": "object", - "properties": { - "row": { - "type": "integer" - }, - "field": { - "type": "string" - }, - "message": { - "type": "string" - } - } - } - }, - "pets": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "species": { - "type": "string" - } - } - } - } - } - } - } - } - ] - } - } + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + }, + "example": { + "success": false, + "error": { + "code": "RATE_LIMITED", + "message": "Too many requests. Please wait." + }, + "timestamp": "2024-01-15T10:30:00Z" } - }, - "201": { - "description": "Import complete (at least one row imported)", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ApiResponse" - } - } + } + } + }, + "InternalServerError": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + }, + "example": { + "success": false, + "error": { + "code": "INTERNAL_ERROR", + "message": "Server error. Please try again later." + }, + "timestamp": "2024-01-15T10:30:00Z" } - }, - "400": { - "$ref": "#/components/responses/ValidationError" - }, - "401": { - "$ref": "#/components/responses/Unauthorized" - }, - "403": { - "$ref": "#/components/responses/Forbidden" } } } } - } -} \ No newline at end of file + }, + "security": [ + { + "BearerAuth": [] + } + ] +} diff --git a/package-lock.json b/package-lock.json index 178d1e2c..6d68cea1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -138,7 +138,7 @@ "lint-staged": "15.5.1", "msw": "^2.13.6", "prettier": "3.5.3", - "react-native-worklets": "^0.10.0", + "react-native-worklets": "^0.9.2", "react-test-renderer": "^19.2.7", "supertest": "^7.2.2", "ts-jest": "^29.4.6", @@ -237,9 +237,9 @@ } }, "node_modules/@apollo/server": { - "version": "4.12.2", - "resolved": "https://registry.npmjs.org/@apollo/server/-/server-4.12.2.tgz", - "integrity": "sha512-jKRlf+sBMMdKYrjMoiWKne42Eb6paBfDOr08KJnUaeaiyWFj+/040FjVPQI7YGLfdwnYIsl1NUUqS2UdgezJDg==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@apollo/server/-/server-4.13.0.tgz", + "integrity": "sha512-t4GzaRiYIcPwYy40db6QjZzgvTr9ztDKBddykUXmBb2SVjswMKXbkaJ5nPeHqmT3awr9PAaZdCZdZhRj55I/8A==", "deprecated": "Apollo Server v4 is end-of-life since January 26, 2026. As long as you are already using a non-EOL version of Node.js, upgrading to v5 should take only a few minutes. See https://www.apollographql.com/docs/apollo-server/previous-versions for details.", "license": "MIT", "dependencies": { @@ -258,6 +258,7 @@ "@types/express-serve-static-core": "^4.17.30", "@types/node-fetch": "^2.6.1", "async-retry": "^1.2.1", + "content-type": "^1.0.5", "cors": "^2.8.5", "express": "^4.21.1", "loglevel": "^1.6.8", @@ -841,18 +842,20 @@ } }, "node_modules/@babel/core": { - "version": "7.29.0", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.0", - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-module-transforms": "^7.28.6", - "@babel/helpers": "^7.28.6", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/traverse": "^7.29.0", - "@babel/types": "^7.29.0", + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", @@ -869,10 +872,12 @@ } }, "node_modules/@babel/core/node_modules/@babel/code-frame": { - "version": "7.29.0", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.28.5", + "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" }, @@ -1172,11 +1177,13 @@ } }, "node_modules/@babel/helpers": { - "version": "7.28.6", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", + "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", "license": "MIT", "dependencies": { - "@babel/template": "^7.28.6", - "@babel/types": "^7.28.6" + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -2966,21 +2973,21 @@ } }, "node_modules/@emnapi/core": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", - "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", + "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==", "dev": true, "license": "MIT", "optional": true, "dependencies": { - "@emnapi/wasi-threads": "1.2.1", + "@emnapi/wasi-threads": "1.2.2", "tslib": "^2.4.0" } }, "node_modules/@emnapi/runtime": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", - "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", + "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", "license": "MIT", "optional": true, "dependencies": { @@ -2988,9 +2995,9 @@ } }, "node_modules/@emnapi/wasi-threads": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", - "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", + "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", "dev": true, "license": "MIT", "optional": true, @@ -3661,6 +3668,203 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@expo/cli": { + "version": "56.1.16", + "resolved": "https://registry.npmjs.org/@expo/cli/-/cli-56.1.16.tgz", + "integrity": "sha512-VBQn0mqAwc67b9Cn0RVXyeodghomAx5xGRhA/bXaQzuxDjMQk0zIOb6pXMZX7yiIwJW66UZt/zQiJNSv6aWJYw==", + "license": "MIT", + "dependencies": { + "@expo/code-signing-certificates": "^0.0.6", + "@expo/config": "~56.0.9", + "@expo/config-plugins": "~56.0.9", + "@expo/devcert": "^1.2.1", + "@expo/env": "~2.3.0", + "@expo/image-utils": "^0.10.1", + "@expo/inline-modules": "^0.0.12", + "@expo/json-file": "^10.2.0", + "@expo/log-box": "^56.0.13", + "@expo/metro": "~56.0.0", + "@expo/metro-config": "~56.0.14", + "@expo/metro-file-map": "^56.0.3", + "@expo/osascript": "^2.6.0", + "@expo/package-manager": "^1.12.1", + "@expo/plist": "^0.7.0", + "@expo/prebuild-config": "^56.0.16", + "@expo/require-utils": "^56.1.3", + "@expo/router-server": "^56.0.14", + "@expo/schema-utils": "^56.0.0", + "@expo/spawn-async": "^1.8.0", + "@expo/ws-tunnel": "^2.0.0", + "@expo/xcpretty": "^4.4.4", + "@react-native/dev-middleware": "0.85.3", + "accepts": "^1.3.8", + "arg": "^5.0.2", + "bplist-creator": "0.1.0", + "bplist-parser": "^0.3.1", + "chalk": "^4.0.0", + "ci-info": "^3.3.0", + "compression": "^1.7.4", + "connect": "^3.7.0", + "debug": "^4.3.4", + "dnssd-advertise": "^1.1.4", + "expo-server": "^56.0.5", + "fetch-nodeshim": "^0.4.10", + "getenv": "^2.0.0", + "glob": "^13.0.0", + "lan-network": "^0.2.1", + "multitars": "^1.0.0", + "node-forge": "^1.3.3", + "npm-package-arg": "^11.0.0", + "ora": "^3.4.0", + "picomatch": "^4.0.4", + "pretty-format": "^29.7.0", + "progress": "^2.0.3", + "prompts": "^2.3.2", + "resolve-from": "^5.0.0", + "semver": "^7.6.0", + "send": "^0.19.0", + "slugify": "^1.3.4", + "stacktrace-parser": "^0.1.10", + "structured-headers": "^0.4.1", + "terminal-link": "^2.1.1", + "toqr": "^0.1.1", + "wrap-ansi": "^7.0.0", + "ws": "^8.12.1", + "zod": "^3.25.76" + }, + "bin": { + "expo-internal": "main.js" + }, + "peerDependencies": { + "expo": "*", + "expo-router": "*", + "react-native": "*" + }, + "peerDependenciesMeta": { + "expo-router": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, + "node_modules/@expo/cli/node_modules/@babel/code-frame": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@expo/cli/node_modules/@expo/config": { + "version": "56.0.9", + "resolved": "https://registry.npmjs.org/@expo/config/-/config-56.0.9.tgz", + "integrity": "sha512-/lqFeWGSrhpKJVP8tTN8LjuoIe8u8q2w7FzBL0C+wHgl+WM8l1qUIEYWy/sMvsG/NbpUIUsDHJRhQvOkU58eIw==", + "license": "MIT", + "dependencies": { + "@expo/config-plugins": "~56.0.8", + "@expo/config-types": "^56.0.5", + "@expo/json-file": "^10.2.0", + "@expo/require-utils": "^56.1.3", + "deepmerge": "^4.3.1", + "getenv": "^2.0.0", + "glob": "^13.0.0", + "resolve-workspace-root": "^2.0.0", + "semver": "^7.6.0", + "slugify": "^1.3.4" + } + }, + "node_modules/@expo/cli/node_modules/@expo/config-plugins": { + "version": "56.0.9", + "resolved": "https://registry.npmjs.org/@expo/config-plugins/-/config-plugins-56.0.9.tgz", + "integrity": "sha512-/6a/S9USwx8OC9tGjHxbviLFiBHyueN3aoNWMLvWDEJoZ1CIVW800ZBzwXq/FYNK2qzcN1LxFmQtzD1zeFQKNA==", + "license": "MIT", + "dependencies": { + "@expo/config-types": "^56.0.6", + "@expo/json-file": "~10.2.0", + "@expo/plist": "^0.7.0", + "@expo/require-utils": "^56.1.3", + "@expo/sdk-runtime-versions": "^1.0.0", + "chalk": "^4.1.2", + "debug": "^4.3.5", + "getenv": "^2.0.0", + "glob": "^13.0.0", + "semver": "^7.5.4", + "slugify": "^1.6.6", + "xcode": "^3.0.1", + "xml2js": "0.6.0" + } + }, + "node_modules/@expo/cli/node_modules/@expo/config-types": { + "version": "56.0.6", + "resolved": "https://registry.npmjs.org/@expo/config-types/-/config-types-56.0.6.tgz", + "integrity": "sha512-4Y6Aum5J4Re5NnxGVofRNe1aDwUBOmWhQYkynZsqzRtX/zEA1ADUeyHXuEckv9YD9djiyT7bKtLt5gKL3mA6VQ==", + "license": "MIT" + }, + "node_modules/@expo/cli/node_modules/@expo/env": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@expo/env/-/env-2.3.0.tgz", + "integrity": "sha512-9HnnIbzwTTdbwSjNLXTk0fPm9ZwMJ7c1/31tsni8HZ8Q62KzYCyspahH+V365vg5J6lr001DzNwBxVWSaYCQLg==", + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "debug": "^4.3.4", + "getenv": "^2.0.0" + }, + "engines": { + "node": ">=20.12.0" + } + }, + "node_modules/@expo/cli/node_modules/@expo/json-file": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/@expo/json-file/-/json-file-10.2.0.tgz", + "integrity": "sha512-S6XzKe3R9GQeHiUPXc3xJjOv2VJhOEwFYf7xdC2z2cUqt3kZJ9mSO877sNQloVdnW/SUCtPY3bexlM7nwq+CAQ==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.20.0", + "json5": "^2.2.3" + } + }, + "node_modules/@expo/cli/node_modules/@expo/plist": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@expo/plist/-/plist-0.7.0.tgz", + "integrity": "sha512-vrpryU1GoqSIRNqRB2D3IjXDmzNYfiQpEF6AH/xknlD7eiYmEDt3mb26V7cLcedcPG8PY/1xWHdBXVQJfEAh6Q==", + "license": "MIT", + "dependencies": { + "@xmldom/xmldom": "^0.8.8", + "base64-js": "^1.5.1", + "xmlbuilder": "^15.1.1" + } + }, + "node_modules/@expo/cli/node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@expo/cli/node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/@expo/code-signing-certificates": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/@expo/code-signing-certificates/-/code-signing-certificates-0.0.6.tgz", @@ -3788,15 +3992,15 @@ } }, "node_modules/@expo/expo-modules-macros-plugin": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/@expo/expo-modules-macros-plugin/-/expo-modules-macros-plugin-0.0.9.tgz", - "integrity": "sha512-odai6D7ng/gA7At8ukFcWcauNEeDdyVqzVPbQxDkyU2NTJ4kgphA4I5iigS5C4LXFicSIzEt2nzdlLM8sjsTdA==", + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@expo/expo-modules-macros-plugin/-/expo-modules-macros-plugin-0.2.2.tgz", + "integrity": "sha512-4IMzPDIo/VOXREQjsJtliSfqYVZvfzU2SLFS/9sKMWF848S8CHx+e/E+Vf0TcMvpWCCKX5umyqxb13KJJ+YUzg==", "license": "MIT" }, "node_modules/@expo/fingerprint": { - "version": "0.19.3", - "resolved": "https://registry.npmjs.org/@expo/fingerprint/-/fingerprint-0.19.3.tgz", - "integrity": "sha512-w9Au2IVrtc0Ct+WRa05DVHGNHXYq6VyPZWuFP+5x055OeZ5q6k5Yg+aJ1gfShmjdOhDftRcsvmWmTdTZlWaTZw==", + "version": "0.19.4", + "resolved": "https://registry.npmjs.org/@expo/fingerprint/-/fingerprint-0.19.4.tgz", + "integrity": "sha512-PsowRlO8+S7JlO8go7yhNEXp7sqlsWDE2AlCwoss7zH0dcajXFo74Fy0KdXEc4UXK7kKoHD37oDgsZ8aHSLr7A==", "license": "MIT", "dependencies": { "@expo/env": "^2.3.0", @@ -3845,11 +4049,13 @@ } }, "node_modules/@expo/image-utils": { - "version": "0.8.13", + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@expo/image-utils/-/image-utils-0.10.1.tgz", + "integrity": "sha512-YDeefvmYdihS7Wp3ESDUVnOgOSWmj2Cczm9lVNDdm4MqQLdAKm/LPYg83HtFQPfefRlAxyHrQR/O9kIXN9C1Wg==", "license": "MIT", "dependencies": { - "@expo/require-utils": "^55.0.4", - "@expo/spawn-async": "^1.7.2", + "@expo/require-utils": "^56.1.3", + "@expo/spawn-async": "^1.8.0", "chalk": "^4.0.0", "getenv": "^2.0.0", "jimp-compact": "0.16.1", @@ -3858,12 +4064,12 @@ } }, "node_modules/@expo/inline-modules": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/@expo/inline-modules/-/inline-modules-0.0.10.tgz", - "integrity": "sha512-DKEfq877UTAmL/gOT5aFhlLNDg/EVmTSca7JQMKDGR6mjFSAcrmQf4GJNsi6ztiaqj6cTnIfoSz0hAYdnr6RJQ==", + "version": "0.0.12", + "resolved": "https://registry.npmjs.org/@expo/inline-modules/-/inline-modules-0.0.12.tgz", + "integrity": "sha512-SNIZr/HWfIQPTZBwmukItxpc7ws1SgMUywYq1dnQvDknQDjJcuWAasIRFUjsK15yQ1xb4G5CP7VHtbN3V4lENg==", "license": "MIT", "dependencies": { - "@expo/config-plugins": "~56.0.8" + "@expo/config-plugins": "~56.0.9" } }, "node_modules/@expo/inline-modules/node_modules/@babel/code-frame": { @@ -3881,12 +4087,12 @@ } }, "node_modules/@expo/inline-modules/node_modules/@expo/config-plugins": { - "version": "56.0.8", - "resolved": "https://registry.npmjs.org/@expo/config-plugins/-/config-plugins-56.0.8.tgz", - "integrity": "sha512-phTuyBhgVLfqUHMjQkAfRtbyoY6yTxoKja1awtpVnEkoJDxPJuXx1KX5uvq1eZtt4bJQ08OBJ6P95INqRSHpRg==", + "version": "56.0.9", + "resolved": "https://registry.npmjs.org/@expo/config-plugins/-/config-plugins-56.0.9.tgz", + "integrity": "sha512-/6a/S9USwx8OC9tGjHxbviLFiBHyueN3aoNWMLvWDEJoZ1CIVW800ZBzwXq/FYNK2qzcN1LxFmQtzD1zeFQKNA==", "license": "MIT", "dependencies": { - "@expo/config-types": "^56.0.5", + "@expo/config-types": "^56.0.6", "@expo/json-file": "~10.2.0", "@expo/plist": "^0.7.0", "@expo/require-utils": "^56.1.3", @@ -3902,9 +4108,9 @@ } }, "node_modules/@expo/inline-modules/node_modules/@expo/config-types": { - "version": "56.0.5", - "resolved": "https://registry.npmjs.org/@expo/config-types/-/config-types-56.0.5.tgz", - "integrity": "sha512-GsAHO/MwW9ZRdgnmyfRXqVGLCP/zejD6rWnp5OROp8mBGRObKm4HfrjlUyT1skjMwCj1OrURx9ZfIc6yeBAkIA==", + "version": "56.0.6", + "resolved": "https://registry.npmjs.org/@expo/config-types/-/config-types-56.0.6.tgz", + "integrity": "sha512-4Y6Aum5J4Re5NnxGVofRNe1aDwUBOmWhQYkynZsqzRtX/zEA1ADUeyHXuEckv9YD9djiyT7bKtLt5gKL3mA6VQ==", "license": "MIT" }, "node_modules/@expo/inline-modules/node_modules/@expo/json-file": { @@ -3928,25 +4134,6 @@ "xmlbuilder": "^15.1.1" } }, - "node_modules/@expo/inline-modules/node_modules/@expo/require-utils": { - "version": "56.1.3", - "resolved": "https://registry.npmjs.org/@expo/require-utils/-/require-utils-56.1.3.tgz", - "integrity": "sha512-KyLeOn/zzQSvuPpV5YhB/FPKnpQytno4luN918bGdPDssLBoS3N/0UbC3W0rJAn9kSFu+XpfR81eABRVsSdfgQ==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.20.0", - "@babel/core": "^7.25.2", - "@babel/plugin-transform-modules-commonjs": "^7.24.8" - }, - "peerDependencies": { - "typescript": "^5.0.0 || ^5.0.0-0 || ^6.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, "node_modules/@expo/json-file": { "version": "10.0.13", "license": "MIT", @@ -4010,12 +4197,12 @@ } }, "node_modules/@expo/local-build-cache-provider/node_modules/@expo/config-plugins": { - "version": "56.0.8", - "resolved": "https://registry.npmjs.org/@expo/config-plugins/-/config-plugins-56.0.8.tgz", - "integrity": "sha512-phTuyBhgVLfqUHMjQkAfRtbyoY6yTxoKja1awtpVnEkoJDxPJuXx1KX5uvq1eZtt4bJQ08OBJ6P95INqRSHpRg==", + "version": "56.0.9", + "resolved": "https://registry.npmjs.org/@expo/config-plugins/-/config-plugins-56.0.9.tgz", + "integrity": "sha512-/6a/S9USwx8OC9tGjHxbviLFiBHyueN3aoNWMLvWDEJoZ1CIVW800ZBzwXq/FYNK2qzcN1LxFmQtzD1zeFQKNA==", "license": "MIT", "dependencies": { - "@expo/config-types": "^56.0.5", + "@expo/config-types": "^56.0.6", "@expo/json-file": "~10.2.0", "@expo/plist": "^0.7.0", "@expo/require-utils": "^56.1.3", @@ -4031,9 +4218,9 @@ } }, "node_modules/@expo/local-build-cache-provider/node_modules/@expo/config-types": { - "version": "56.0.5", - "resolved": "https://registry.npmjs.org/@expo/config-types/-/config-types-56.0.5.tgz", - "integrity": "sha512-GsAHO/MwW9ZRdgnmyfRXqVGLCP/zejD6rWnp5OROp8mBGRObKm4HfrjlUyT1skjMwCj1OrURx9ZfIc6yeBAkIA==", + "version": "56.0.6", + "resolved": "https://registry.npmjs.org/@expo/config-types/-/config-types-56.0.6.tgz", + "integrity": "sha512-4Y6Aum5J4Re5NnxGVofRNe1aDwUBOmWhQYkynZsqzRtX/zEA1ADUeyHXuEckv9YD9djiyT7bKtLt5gKL3mA6VQ==", "license": "MIT" }, "node_modules/@expo/local-build-cache-provider/node_modules/@expo/json-file": { @@ -4057,34 +4244,15 @@ "xmlbuilder": "^15.1.1" } }, - "node_modules/@expo/local-build-cache-provider/node_modules/@expo/require-utils": { - "version": "56.1.3", - "resolved": "https://registry.npmjs.org/@expo/require-utils/-/require-utils-56.1.3.tgz", - "integrity": "sha512-KyLeOn/zzQSvuPpV5YhB/FPKnpQytno4luN918bGdPDssLBoS3N/0UbC3W0rJAn9kSFu+XpfR81eABRVsSdfgQ==", + "node_modules/@expo/log-box": { + "version": "56.0.13", + "resolved": "https://registry.npmjs.org/@expo/log-box/-/log-box-56.0.13.tgz", + "integrity": "sha512-QWRZSpWPyjkDLVQio4R7oAzg/Av2MOt/DciFkfjr8qQ3qxGVn1Rt1oHP/80hvcWDcHFV7N6PqpyxRXw6nbxzKQ==", "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.20.0", - "@babel/core": "^7.25.2", - "@babel/plugin-transform-modules-commonjs": "^7.24.8" - }, - "peerDependencies": { - "typescript": "^5.0.0 || ^5.0.0-0 || ^6.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@expo/log-box": { - "version": "56.0.12", - "resolved": "https://registry.npmjs.org/@expo/log-box/-/log-box-56.0.12.tgz", - "integrity": "sha512-budE6AGmJbpOJfGSOz+JVP3+FevElT82IEIg+ukQ4gZpW/dGO7QX1unFjanKdSaYgudBwJ4FCFGMwWhW/1tXVQ==", - "license": "MIT", - "dependencies": { - "@expo/dom-webview": "^56.0.5", - "anser": "^1.4.9", - "stacktrace-parser": "^0.1.10" + "@expo/dom-webview": "^56.0.5", + "anser": "^1.4.9", + "stacktrace-parser": "^0.1.10" }, "peerDependencies": { "@expo/dom-webview": "^56.0.5", @@ -4116,9 +4284,9 @@ } }, "node_modules/@expo/metro-config": { - "version": "56.0.13", - "resolved": "https://registry.npmjs.org/@expo/metro-config/-/metro-config-56.0.13.tgz", - "integrity": "sha512-OPyNYiex/6Ms8zT2POdIZsLhcAZYk7O+yJvpz5uG/4QRA7aiESfCy1I+0YHewMlR4P1YQeyxIrfTurs6m9xfZA==", + "version": "56.0.14", + "resolved": "https://registry.npmjs.org/@expo/metro-config/-/metro-config-56.0.14.tgz", + "integrity": "sha512-O3CIHruaTJhswPAf/nf3i8QQ3f2jl+mEwSea1eb3khuplabdy/wTQz+JvHN8VGUFyg7JKwUGU1QfO6T3JiSQqA==", "license": "MIT", "dependencies": { "@babel/code-frame": "^7.20.0", @@ -4141,7 +4309,6 @@ "hermes-parser": "^0.33.3", "jsc-safe-url": "^0.2.4", "lightningcss": "^1.30.1", - "msgpackr": "^2.0.1", "picomatch": "^4.0.4", "postcss": "^8.5.14", "resolve-from": "^5.0.0" @@ -4188,12 +4355,12 @@ } }, "node_modules/@expo/metro-config/node_modules/@expo/config-plugins": { - "version": "56.0.8", - "resolved": "https://registry.npmjs.org/@expo/config-plugins/-/config-plugins-56.0.8.tgz", - "integrity": "sha512-phTuyBhgVLfqUHMjQkAfRtbyoY6yTxoKja1awtpVnEkoJDxPJuXx1KX5uvq1eZtt4bJQ08OBJ6P95INqRSHpRg==", + "version": "56.0.9", + "resolved": "https://registry.npmjs.org/@expo/config-plugins/-/config-plugins-56.0.9.tgz", + "integrity": "sha512-/6a/S9USwx8OC9tGjHxbviLFiBHyueN3aoNWMLvWDEJoZ1CIVW800ZBzwXq/FYNK2qzcN1LxFmQtzD1zeFQKNA==", "license": "MIT", "dependencies": { - "@expo/config-types": "^56.0.5", + "@expo/config-types": "^56.0.6", "@expo/json-file": "~10.2.0", "@expo/plist": "^0.7.0", "@expo/require-utils": "^56.1.3", @@ -4209,9 +4376,9 @@ } }, "node_modules/@expo/metro-config/node_modules/@expo/config-types": { - "version": "56.0.5", - "resolved": "https://registry.npmjs.org/@expo/config-types/-/config-types-56.0.5.tgz", - "integrity": "sha512-GsAHO/MwW9ZRdgnmyfRXqVGLCP/zejD6rWnp5OROp8mBGRObKm4HfrjlUyT1skjMwCj1OrURx9ZfIc6yeBAkIA==", + "version": "56.0.6", + "resolved": "https://registry.npmjs.org/@expo/config-types/-/config-types-56.0.6.tgz", + "integrity": "sha512-4Y6Aum5J4Re5NnxGVofRNe1aDwUBOmWhQYkynZsqzRtX/zEA1ADUeyHXuEckv9YD9djiyT7bKtLt5gKL3mA6VQ==", "license": "MIT" }, "node_modules/@expo/metro-config/node_modules/@expo/env": { @@ -4249,25 +4416,6 @@ "xmlbuilder": "^15.1.1" } }, - "node_modules/@expo/metro-config/node_modules/@expo/require-utils": { - "version": "56.1.3", - "resolved": "https://registry.npmjs.org/@expo/require-utils/-/require-utils-56.1.3.tgz", - "integrity": "sha512-KyLeOn/zzQSvuPpV5YhB/FPKnpQytno4luN918bGdPDssLBoS3N/0UbC3W0rJAn9kSFu+XpfR81eABRVsSdfgQ==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.20.0", - "@babel/core": "^7.25.2", - "@babel/plugin-transform-modules-commonjs": "^7.24.8" - }, - "peerDependencies": { - "typescript": "^5.0.0 || ^5.0.0-0 || ^6.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, "node_modules/@expo/metro-file-map": { "version": "56.0.3", "resolved": "https://registry.npmjs.org/@expo/metro-file-map/-/metro-file-map-56.0.3.tgz", @@ -4342,19 +4490,19 @@ } }, "node_modules/@expo/prebuild-config": { - "version": "56.0.14", - "resolved": "https://registry.npmjs.org/@expo/prebuild-config/-/prebuild-config-56.0.14.tgz", - "integrity": "sha512-JHdMqR7Mf5ApLC50ZwTL0Z86ezrHOMYwoSHcWT6Pha/+1TcC+/J+i7vjhP06wGXQ2Kvjt74p/3mKg2Pd12KjhQ==", + "version": "56.0.16", + "resolved": "https://registry.npmjs.org/@expo/prebuild-config/-/prebuild-config-56.0.16.tgz", + "integrity": "sha512-ce9ENfPWO4WUWUVQz0OaqL3KYZ7YofP8O35ncnn7CHCaKwQ7BqxcCGJbh+qvP1UjlWeNB3CjHPrXXJ3bnZwlJw==", "license": "MIT", "dependencies": { "@expo/config": "~56.0.9", - "@expo/config-plugins": "~56.0.8", - "@expo/config-types": "^56.0.5", + "@expo/config-plugins": "~56.0.9", + "@expo/config-types": "^56.0.6", "@expo/image-utils": "^0.10.1", "@expo/json-file": "^10.2.0", "@react-native/normalize-colors": "0.85.3", "debug": "^4.3.1", - "expo-modules-autolinking": "~56.0.14", + "expo-modules-autolinking": "~56.0.16", "resolve-from": "^5.0.0", "semver": "^7.6.0" } @@ -4392,12 +4540,12 @@ } }, "node_modules/@expo/prebuild-config/node_modules/@expo/config-plugins": { - "version": "56.0.8", - "resolved": "https://registry.npmjs.org/@expo/config-plugins/-/config-plugins-56.0.8.tgz", - "integrity": "sha512-phTuyBhgVLfqUHMjQkAfRtbyoY6yTxoKja1awtpVnEkoJDxPJuXx1KX5uvq1eZtt4bJQ08OBJ6P95INqRSHpRg==", + "version": "56.0.9", + "resolved": "https://registry.npmjs.org/@expo/config-plugins/-/config-plugins-56.0.9.tgz", + "integrity": "sha512-/6a/S9USwx8OC9tGjHxbviLFiBHyueN3aoNWMLvWDEJoZ1CIVW800ZBzwXq/FYNK2qzcN1LxFmQtzD1zeFQKNA==", "license": "MIT", "dependencies": { - "@expo/config-types": "^56.0.5", + "@expo/config-types": "^56.0.6", "@expo/json-file": "~10.2.0", "@expo/plist": "^0.7.0", "@expo/require-utils": "^56.1.3", @@ -4413,26 +4561,11 @@ } }, "node_modules/@expo/prebuild-config/node_modules/@expo/config-types": { - "version": "56.0.5", - "resolved": "https://registry.npmjs.org/@expo/config-types/-/config-types-56.0.5.tgz", - "integrity": "sha512-GsAHO/MwW9ZRdgnmyfRXqVGLCP/zejD6rWnp5OROp8mBGRObKm4HfrjlUyT1skjMwCj1OrURx9ZfIc6yeBAkIA==", + "version": "56.0.6", + "resolved": "https://registry.npmjs.org/@expo/config-types/-/config-types-56.0.6.tgz", + "integrity": "sha512-4Y6Aum5J4Re5NnxGVofRNe1aDwUBOmWhQYkynZsqzRtX/zEA1ADUeyHXuEckv9YD9djiyT7bKtLt5gKL3mA6VQ==", "license": "MIT" }, - "node_modules/@expo/prebuild-config/node_modules/@expo/image-utils": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@expo/image-utils/-/image-utils-0.10.1.tgz", - "integrity": "sha512-YDeefvmYdihS7Wp3ESDUVnOgOSWmj2Cczm9lVNDdm4MqQLdAKm/LPYg83HtFQPfefRlAxyHrQR/O9kIXN9C1Wg==", - "license": "MIT", - "dependencies": { - "@expo/require-utils": "^56.1.3", - "@expo/spawn-async": "^1.8.0", - "chalk": "^4.0.0", - "getenv": "^2.0.0", - "jimp-compact": "0.16.1", - "parse-png": "^2.1.0", - "semver": "^7.6.0" - } - }, "node_modules/@expo/prebuild-config/node_modules/@expo/json-file": { "version": "10.2.0", "resolved": "https://registry.npmjs.org/@expo/json-file/-/json-file-10.2.0.tgz", @@ -4454,7 +4587,7 @@ "xmlbuilder": "^15.1.1" } }, - "node_modules/@expo/prebuild-config/node_modules/@expo/require-utils": { + "node_modules/@expo/require-utils": { "version": "56.1.3", "resolved": "https://registry.npmjs.org/@expo/require-utils/-/require-utils-56.1.3.tgz", "integrity": "sha512-KyLeOn/zzQSvuPpV5YhB/FPKnpQytno4luN918bGdPDssLBoS3N/0UbC3W0rJAn9kSFu+XpfR81eABRVsSdfgQ==", @@ -4473,28 +4606,13 @@ } } }, - "node_modules/@expo/require-utils": { - "version": "55.0.4", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.20.0", - "@babel/core": "^7.25.2", - "@babel/plugin-transform-modules-commonjs": "^7.24.8" - }, - "peerDependencies": { - "typescript": "^5.0.0 || ^5.0.0-0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, "node_modules/@expo/require-utils/node_modules/@babel/code-frame": { - "version": "7.29.0", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.28.5", + "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" }, @@ -4502,6 +4620,40 @@ "node": ">=6.9.0" } }, + "node_modules/@expo/router-server": { + "version": "56.0.14", + "resolved": "https://registry.npmjs.org/@expo/router-server/-/router-server-56.0.14.tgz", + "integrity": "sha512-2UCTtZfcq1ZPgp3wk8/+sq9DvFI9UxrPr1jcEKMAF2DGAJLosnpc8GWNNg2hkjt6SHUOdFHIPxujWPYyho2y3A==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.4" + }, + "peerDependencies": { + "@expo/metro-runtime": "^56.0.15", + "expo": "*", + "expo-constants": "^56.0.18", + "expo-font": "^56.0.6", + "expo-router": "*", + "expo-server": "^56.0.5", + "react": "*", + "react-dom": "*", + "react-server-dom-webpack": "~19.0.1 || ~19.1.2 || ~19.2.1" + }, + "peerDependenciesMeta": { + "@expo/metro-runtime": { + "optional": true + }, + "expo-router": { + "optional": true + }, + "react-dom": { + "optional": true + }, + "react-server-dom-webpack": { + "optional": true + } + } + }, "node_modules/@expo/schema-utils": { "version": "56.0.1", "resolved": "https://registry.npmjs.org/@expo/schema-utils/-/schema-utils-56.0.1.tgz", @@ -4542,10 +4694,13 @@ } }, "node_modules/@expo/ws-tunnel": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@expo/ws-tunnel/-/ws-tunnel-1.0.6.tgz", - "integrity": "sha512-nDRbLmSrJar7abvUjp3smDwH8HcbZcoOEa5jVPUv9/9CajgmWw20JNRwTuBRzWIWIkEJDkz20GoNA+tSwUqk0Q==", - "license": "MIT" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@expo/ws-tunnel/-/ws-tunnel-2.0.0.tgz", + "integrity": "sha512-j+JfTRdCk820J9dU0sA2SqshQIKFOMo7ED84w9MJFcebfbNQgsLztEY/SABDkGnjatrW4xGqnUhVRxSBVyCkXw==", + "license": "MIT", + "peerDependencies": { + "ws": "^8.0.0" + } }, "node_modules/@expo/xcpretty": { "version": "4.4.4", @@ -6643,7 +6798,9 @@ } }, "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { - "version": "3.14.2", + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.15.0.tgz", + "integrity": "sha512-ttBQIIQPDeLjpPOohtUdXuXUVoA2uIB6fEH9HyJ7234s5mBJ5wTx20njxplLZQgLaOfpmPQA7X2t5AX6tIPbog==", "dev": true, "license": "MIT", "dependencies": { @@ -6870,7 +7027,9 @@ "license": "MIT" }, "node_modules/@jest/reporters/node_modules/brace-expansion": { - "version": "1.1.12", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", + "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", "dev": true, "license": "MIT", "dependencies": { @@ -7139,84 +7298,6 @@ "node": ">=16.20.0" } }, - "node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.4.tgz", - "integrity": "sha512-LCkGo6JDfaBhgST7UpPWgNgLINpcpabaHfyz5OBx75nUYxBsaEPxjnyNjWpeb/xBup/682QnBfRBy2/LvPutZQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@msgpackr-extract/msgpackr-extract-darwin-x64": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.4.tgz", - "integrity": "sha512-zExlW9zUJKZH/tOtVMttwjKa4Xm/3KcNjnE3dPN92uCktwavMxpgCA3MoJK/DOnTWsQgo224OaST27/mPNAf+w==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.4.tgz", - "integrity": "sha512-Tg3yX65f5GbtXLkrYEHE5oibZG9epyYWas7FogTTEJeDEF9JlXJzKgXaNhT3UXlTOeA+AfZpYZYZ0uPj7Cfquw==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm64": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.4.tgz", - "integrity": "sha512-dgX0P/9wGPJeHFBG+ZmhgE6bmtMt7NP5CRBGyyktpopdk/mW4POnrpQsSLtKI1dwpc+pPLuXHDh6vvskyQE/sw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@msgpackr-extract/msgpackr-extract-linux-x64": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.4.tgz", - "integrity": "sha512-8TNXMEjJc3QEy7R/x1INhgiU+XakDAFUzBhaz7+Rbrs8NH5UQeHQxxmzsSBJGyV6I1jW79undiQm8tOI+D+8FQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@msgpackr-extract/msgpackr-extract-win32-x64": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.4.tgz", - "integrity": "sha512-CmCXPQrkbwExx3j946/PtHWHbYJiCRBRDl4BlkRQcJB/YOwQxJRTpoo7aTsortjgoJ1x7opzTSxn7C+ASSLVjQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, "node_modules/@mswjs/interceptors": { "version": "0.41.6", "dev": true, @@ -7423,14 +7504,14 @@ } }, "node_modules/@napi-rs/wasm-runtime": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", - "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz", + "integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==", "dev": true, "license": "MIT", "optional": true, "dependencies": { - "@tybys/wasm-util": "^0.10.1" + "@tybys/wasm-util": "^0.10.3" }, "funding": { "type": "github", @@ -7556,9 +7637,9 @@ } }, "node_modules/@opentelemetry/core": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.7.1.tgz", - "integrity": "sha512-QAqIj32AtK6+pEVNG7EOVxHdE06RP+FM5qpiEJ4RtDcFIqKUZHYhl7/7UY5efhwmwNAg7j8QbJVBLxMerc0+gw==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.8.0.tgz", + "integrity": "sha512-hd1Lfh8p545nNz+jq1Ejfz+Mn1hyLuxYn1YzTfFNrxr8urEWMNQLPf1Th8kjOH+HxwawCrtgBp8JpBUR4ZSgww==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" @@ -7588,12 +7669,12 @@ } }, "node_modules/@opentelemetry/resources": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.7.1.tgz", - "integrity": "sha512-DeT6KKolmC4e/dRQvMQ/RwlnzhaqeiFOXY5ngoOPJ07GgVVKxZOg9EcrNZb5aTzUn+iCrJldAgOfQm1O/QfPAQ==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.8.0.tgz", + "integrity": "sha512-qmXQ27ilDbUK/vGMqwL8D4/rhn76C+sherM4wTbjlfknR8Nvfc/hCxjRJPhkzZzUsPiNg16SA31NxMabwttRjg==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.7.1", + "@opentelemetry/core": "2.8.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { @@ -7604,13 +7685,13 @@ } }, "node_modules/@opentelemetry/sdk-trace-base": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.7.1.tgz", - "integrity": "sha512-NAYIlsF8MPUsKqJMiDQJTMPOmlbawC1Iz/omMLygZ1C9am8fTKYjTaI+OZM+WTY3t3Glo0wnOg/6/pac6RGPPw==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.8.0.tgz", + "integrity": "sha512-mhU4jp+vW0mGbFRd+GeXHvmfA4aDqWjBjLC3pE5XMpLs0IE2ryYb019Ts2AQrOq67gaTF25D91+fgvEHDZEnuQ==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.7.1", - "@opentelemetry/resources": "2.7.1", + "@opentelemetry/core": "2.8.0", + "@opentelemetry/resources": "2.8.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { @@ -7698,7 +7779,9 @@ } }, "node_modules/@oxc-project/types": { - "version": "0.127.0", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.137.0.tgz", + "integrity": "sha512-WT+Gb24i8hmvo85AIv2oEYouEXkRlKAlT9WaCa3TfLgNCN+GhrJOGZuIlMouAh38Qe4QOx26eUOVsq70qXrywA==", "dev": true, "license": "MIT", "funding": { @@ -8102,9 +8185,9 @@ "license": "MIT" }, "node_modules/@rolldown/binding-android-arm64": { - "version": "1.0.0-rc.17", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.17.tgz", - "integrity": "sha512-s70pVGhw4zqGeFnXWvAzJDlvxhlRollagdCCKRgOsgUOH3N1l0LIxf83AtGzmb5SiVM4Hjl5HyarMRfdfj3DaQ==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.3.tgz", + "integrity": "sha512-DT6Z3PhvioeHMvxo+xHc3KtqggrI7CCTXCmC2h/5zUlp5jVitv7XEy+9q5/7v8IolhlioawpMo8Kg0EEBy7J0g==", "cpu": [ "arm64" ], @@ -8119,9 +8202,9 @@ } }, "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.0.0-rc.17", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.17.tgz", - "integrity": "sha512-4ksWc9n0mhlZpZ9PMZgTGjeOPRu8MB1Z3Tz0Mo02eWfWCHMW1zN82Qz/pL/rC+yQa+8ZnutMF0JjJe7PjwasYw==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.3.tgz", + "integrity": "sha512-0NwgwsjM7LrsuVnXMK3koTpagBNOhloc/BNjKqZjv4V5zI5r13qx69uVhRx+o5Z0yy4Hzq+lpy7TAgUG/ocvrw==", "cpu": [ "arm64" ], @@ -8136,9 +8219,9 @@ } }, "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.0.0-rc.17", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.17.tgz", - "integrity": "sha512-SUSDOI6WwUVNcWxd02QEBjLdY1VPHvlEkw6T/8nYG322iYWCTxRb1vzk4E+mWWYehTp7ERibq54LSJGjmouOsw==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.3.tgz", + "integrity": "sha512-YtiBp4disu6V560loT6PjMdiRaWmVvDNrUunAalbiFx2ggeJwxdAsgZMcoGP17uyAsTwAj5V1niksxlHnVQ1Sw==", "cpu": [ "x64" ], @@ -8153,9 +8236,9 @@ } }, "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.0.0-rc.17", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.17.tgz", - "integrity": "sha512-hwnz3nw9dbJ05EDO/PvcjaaewqqDy7Y1rn1UO81l8iIK1GjenME75dl16ajbvSSMfv66WXSRCYKIqfgq2KCfxw==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.3.tgz", + "integrity": "sha512-yD3EkEdXk2LypPxnf/kSZHirarsI8gcPzc62SukhR9VJTyvV+F9Q/GxWNuCojc7sXyuVC4DxRGhdDK4X8VSsbw==", "cpu": [ "x64" ], @@ -8170,9 +8253,9 @@ } }, "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.0.0-rc.17", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.17.tgz", - "integrity": "sha512-IS+W7epTcwANmFSQFrS1SivEXHtl1JtuQA9wlxrZTcNi6mx+FDOYrakGevvvTwgj2JvWiK8B29/qD9BELZPyXQ==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.3.tgz", + "integrity": "sha512-c+8vieQbsD7HNAHKIA34w0GJ9FedFFuJGD+7E6vz7Q3uqAIugL5p45fhlsj4UaAsHpcmlqugBWMhA0/j7o0sIg==", "cpu": [ "arm" ], @@ -8187,9 +8270,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.0.0-rc.17", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.17.tgz", - "integrity": "sha512-e6usGaHKW5BMNZOymS1UcEYGowQMWcgZ71Z17Sl/h2+ZziNJ1a9n3Zvcz6LdRyIW5572wBCTH/Z+bKuZouGk9Q==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.3.tgz", + "integrity": "sha512-50jD0uUwLvur7Zz9LHz17kaAdTPjn5wN93hEgjvmYFRZwiR7ZJYovTd5ipyWJDAnXKvZ+wgc+/Ika6dwSF5OcA==", "cpu": [ "arm64" ], @@ -8204,9 +8287,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.0.0-rc.17", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.17.tgz", - "integrity": "sha512-b/CgbwAJpmrRLp02RPfhbudf5tZnN9nsPWK82znefso832etkem8H7FSZwxrOI9djcdTP7U6YfNhbRnh7djErg==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.3.tgz", + "integrity": "sha512-BO9+oPL8K9poZJBfYPsXNtYjPE5uM3qeehT3aFcW4LITOl+iSqhp0abzjR2nWBUNjIZeKXjAEWBZ64WjNoHd6w==", "cpu": [ "arm64" ], @@ -8221,9 +8304,9 @@ } }, "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.0.0-rc.17", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.17.tgz", - "integrity": "sha512-4EII1iNGRUN5WwGbF/kOh/EIkoDN9HsupgLQoXfY+D1oyJm7/F4t5PYU5n8SWZgG0FEwakyM8pGgwcBYruGTlA==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.3.tgz", + "integrity": "sha512-f3VpLB1vQ0Eo6ecr/6cekLnvYMFF4YBFoVGkfkvPLq1bAkbAwHYQPZKoAmG6OJyTcxxoC+AvezGx/S1obNC0Mw==", "cpu": [ "ppc64" ], @@ -8238,9 +8321,9 @@ } }, "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.0.0-rc.17", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.17.tgz", - "integrity": "sha512-AH8oq3XqQo4IibpVXvPeLDI5pzkpYn0WiZAfT05kFzoJ6tQNzwRdDYQ45M8I/gslbodRZwW8uxLhbSBbkv96rA==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.3.tgz", + "integrity": "sha512-AmurZ26Pqx/RI9N1gzEOCklkKXl927yjfXWUUS0O7Puh8ARM/Ob8qfrD3qnWksScdw6cSrW5PSHE9DyLu7+PtA==", "cpu": [ "s390x" ], @@ -8255,9 +8338,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.0.0-rc.17", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.17.tgz", - "integrity": "sha512-cLnjV3xfo7KslbU41Z7z8BH/E1y5mzUYzAqih1d1MDaIGZRCMqTijqLv76/P7fyHuvUcfGsIpqCdddbxLLK9rA==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.3.tgz", + "integrity": "sha512-JJpqs8bRGITDOdbkNKnlojzBabbOHrqjSvDr0IVsZObE1lBcPjxItUEY9eWIDbxaJ3cGrXPWGfGkIxFijg/URg==", "cpu": [ "x64" ], @@ -8272,9 +8355,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.0.0-rc.17", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.17.tgz", - "integrity": "sha512-0phclDw1spsL7dUB37sIARuis2tAgomCJXAHZlpt8PXZ4Ba0dRP1e+66lsRqrfhISeN9bEGNjQs+T/Fbd7oYGw==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.3.tgz", + "integrity": "sha512-rSJcdjPxzA/by/6/rYs+v+bXU7UjvnbUWz8MJb6kh6+knqB1dCrtHg0uu7C/4haqJvqdkYHQ5IGn+tCH9GLW/g==", "cpu": [ "x64" ], @@ -8289,9 +8372,9 @@ } }, "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.0.0-rc.17", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.17.tgz", - "integrity": "sha512-0ag/hEgXOwgw4t8QyQvUCxvEg+V0KBcA6YuOx9g0r02MprutRF5dyljgm3EmR02O292UX7UeS6HzWHAl6KgyhA==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.3.tgz", + "integrity": "sha512-hQ3/PYkDJICgevvyNcVrihVeqq7k1Pp3VZ9lY+dauAYUJKO+auqApvANhvR1An9BhmqYKvW2Mu1F9u4DXSMLxQ==", "cpu": [ "arm64" ], @@ -8306,9 +8389,9 @@ } }, "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.0.0-rc.17", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.17.tgz", - "integrity": "sha512-LEXei6vo0E5wTGwpkJ4KoT3OZJRnglwldt5ziLzOlc6qqb55z4tWNq2A+PFqCJuvWWdP53CVhG1Z9NtToDPJrA==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.3.tgz", + "integrity": "sha512-Elcv/BtML9lXrV6JuKITc/grN2kYV9gjsQpW8Jfw4ioK0TOkjBjye0nnyqQNy9STNaI20lXNaQBRrD5gSgR0Yg==", "cpu": [ "wasm32" ], @@ -8316,18 +8399,18 @@ "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "1.10.0", - "@emnapi/runtime": "1.10.0", - "@napi-rs/wasm-runtime": "^1.1.4" + "@emnapi/core": "1.11.1", + "@emnapi/runtime": "1.11.1", + "@napi-rs/wasm-runtime": "^1.1.6" }, "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.0.0-rc.17", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.17.tgz", - "integrity": "sha512-gUmyzBl3SPMa6hrqFUth9sVfcLBlYsbMzBx5PlexMroZStgzGqlZ26pYG89rBb45Mnia+oil6YAIFeEWGWhoZA==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.3.tgz", + "integrity": "sha512-2DrEfhluH9yhiaFApmsjsjwrSYbNcY1oFTzYSP1a535jDbV98zCFanA/96TBUd0iDFcxGmw9QRExwGCXz3U+/g==", "cpu": [ "arm64" ], @@ -8342,7 +8425,9 @@ } }, "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.0.0-rc.17", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.3.tgz", + "integrity": "sha512-OL4OMk7UPXOeVGGd3qo5zJyPIljf4AFgk5QAkPPS+OoLuOOozhuaQGC18MxVTnw/06q93gShAJzlwnSCY9YtqA==", "cpu": [ "x64" ], @@ -8357,7 +8442,9 @@ } }, "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-rc.17", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", "dev": true, "license": "MIT" }, @@ -9045,13 +9132,13 @@ } }, "node_modules/@stellar/stellar-sdk": { - "version": "15.0.1", - "resolved": "https://registry.npmjs.org/@stellar/stellar-sdk/-/stellar-sdk-15.0.1.tgz", - "integrity": "sha512-iZjWKXtfohsPh+CX9wRyQNIlXLeA9VyuQB6UMC7AFBD9XnR92eOjnlfeONzk/Bsrkk6+UPlpzSy2MuF+ydHP1A==", + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/@stellar/stellar-sdk/-/stellar-sdk-15.1.0.tgz", + "integrity": "sha512-GsJUcWx2yboVzYdhTe/LHS3V1wVLSHkUkglC5bBoYWGJt31vzIhbSGno60NP9CdCTNkLJdnrsLJ63oA58Zvh5A==", "license": "Apache-2.0", "dependencies": { "@stellar/stellar-base": "^15.0.0", - "axios": "1.14.0", + "axios": "1.15.0", "bignumber.js": "^9.3.1", "commander": "^14.0.3", "eventsource": "^2.0.2", @@ -9068,9 +9155,9 @@ } }, "node_modules/@stellar/stellar-sdk/node_modules/axios": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.14.0.tgz", - "integrity": "sha512-3Y8yrqLSwjuzpXuZ0oIYZ/XGgLwUIBU3uLvbcpb0pidD9ctpShJd43KSlEEkVQg6DS0G9NKyzOvBfUtDKEyHvQ==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.15.0.tgz", + "integrity": "sha512-wWyJDlAatxk30ZJer+GeCWS209sA42X+N5jU2jy6oHTp7ufw8uzUTVFBX9+wTfAlhiJXGS0Bq7X6efruWjuK9Q==", "license": "MIT", "dependencies": { "follow-redirects": "^1.15.11", @@ -10165,9 +10252,9 @@ } }, "node_modules/@tybys/wasm-util": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", - "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", + "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", "dev": true, "license": "MIT", "optional": true, @@ -11044,7 +11131,9 @@ } }, "node_modules/@xmldom/xmldom": { - "version": "0.8.11", + "version": "0.8.13", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.13.tgz", + "integrity": "sha512-KRYzxepc14G/CEpEGc3Yn+JKaAeT63smlDr+vjB8jRfgTBBI9wRj/nkQEO+ucV8p8I9bfKLWp37uHgFrbntPvw==", "license": "MIT", "engines": { "node": ">=10.0.0" @@ -11242,7 +11331,9 @@ } }, "node_modules/anymatch/node_modules/picomatch": { - "version": "2.3.1", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "dev": true, "license": "MIT", "engines": { @@ -11596,11 +11687,14 @@ "license": "MIT" }, "node_modules/axios": { - "version": "1.15.2", + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.18.1.tgz", + "integrity": "sha512-3nTvFlvpn9Zu/RkHUqtc7/+al4UpRW5az71ap5zccp6e8RAYEzhMTecX8Dz1wWDYrPpUoB1HAQEGEAEvUr7S9g==", "license": "MIT", "dependencies": { - "follow-redirects": "^1.15.11", + "follow-redirects": "^1.16.0", "form-data": "^4.0.5", + "https-proxy-agent": "^5.0.1", "proxy-from-env": "^2.1.0" } }, @@ -11616,6 +11710,31 @@ "axios": ">= 0.17.0" } }, + "node_modules/axios/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/axios/node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/b4a": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.1.tgz", @@ -11788,9 +11907,9 @@ } }, "node_modules/babel-preset-expo": { - "version": "56.0.14", - "resolved": "https://registry.npmjs.org/babel-preset-expo/-/babel-preset-expo-56.0.14.tgz", - "integrity": "sha512-+JKVMYf3HajO3tPRA9DlKd/VhZOPTHyTzUo2yZajfMAoQ3l5VEdGVxm2MzX4DXMNKXwsC8GOeTRx7CrO/5dBDA==", + "version": "56.0.15", + "resolved": "https://registry.npmjs.org/babel-preset-expo/-/babel-preset-expo-56.0.15.tgz", + "integrity": "sha512-0MqbQoM6nBUbKvgu2xJ4VixZnUTGTq3HB2WwvOikdO4CiPxbQ+wGA25fOoHHSni5iEFW39wy6y1ookTWlq3wVw==", "license": "MIT", "dependencies": { "@babel/generator": "^7.20.5", @@ -11839,7 +11958,7 @@ "peerDependencies": { "@babel/runtime": "^7.20.0", "expo": "*", - "expo-widgets": "^56.0.16", + "expo-widgets": "^56.0.18", "react-refresh": ">=0.14.0 <1.0.0" }, "peerDependenciesMeta": { @@ -13504,9 +13623,9 @@ } }, "node_modules/dnssd-advertise": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/dnssd-advertise/-/dnssd-advertise-1.1.4.tgz", - "integrity": "sha512-AmGyK9WpNf06WeP5TjHZq/wNzP76OuEeaiTlKr9E/EEelYLczywUKoqRz+DPRq/ErssjT4lU+/W7wzJW+7K/ZA==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/dnssd-advertise/-/dnssd-advertise-1.1.6.tgz", + "integrity": "sha512-Ndrrf6BMPalkQPd/zubL+4YghH2J9NspapQ09uDXwYbvOPkP0oaqf5CkcwJ0b50kS2O3ul6yVu+jz+RY62Cejg==", "license": "MIT" }, "node_modules/doctrine": { @@ -13609,9 +13728,9 @@ } }, "node_modules/engine.io": { - "version": "6.6.8", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.8.tgz", - "integrity": "sha512-2agL3ueZhqxoVrfmntO8yuVj+uNSlIOnhykYHk3Cq0ShYPdUjjUiSJrQvXjq01I9jAuI0Zl2YO8Evv5Mqytm5g==", + "version": "6.6.9", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.9.tgz", + "integrity": "sha512-clKkw4C7nJ22mGgoVcCg6V/W/TxdNyIOTr89k2ONZu81qqkddPFDF0LXcbAwhzPD8DjkiRCjzuiO6Y+fkpD4vg==", "license": "MIT", "dependencies": { "@types/cors": "^2.8.12", @@ -13623,46 +13742,25 @@ "cors": "~2.8.5", "debug": "~4.4.1", "engine.io-parser": "~5.2.1", - "ws": "~8.20.1" + "ws": "~8.21.0" }, "engines": { "node": ">=10.2.0" } }, "node_modules/engine.io-client": { - "version": "6.6.5", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.5.tgz", - "integrity": "sha512-QCwxUDULPlXv8F6tqMMKx5dNkTe6OaBYRMPYeXKBlyOoKvAmE0ac6pW7fFhSscJ/5SI7666/U/B+MElbsrJlIg==", + "version": "6.6.6", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.6.tgz", + "integrity": "sha512-iY6QdftLQ9pyiPoX082bpf/u1UewnOaJrtJIF9T0++QB34lZrj0uP+Q/bj8AlUsAxqhnkTV2BS8SBZSxOmoV5Q==", "license": "MIT", "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.4.1", "engine.io-parser": "~5.2.1", - "ws": "~8.20.1", + "ws": "~8.21.0", "xmlhttprequest-ssl": "~2.1.1" } }, - "node_modules/engine.io-client/node_modules/ws": { - "version": "8.20.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.1.tgz", - "integrity": "sha512-It4dO0K5v//JtTXuPkfEOaI3uUN87iYPnqo/ZzqCoG3g8uhA66QUMs/SrM0YK7/NAu+r4LMh/9dq2A7k+rHs+w==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, "node_modules/engine.io-parser": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", @@ -13694,27 +13792,6 @@ "node": ">= 0.6" } }, - "node_modules/engine.io/node_modules/ws": { - "version": "8.20.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.1.tgz", - "integrity": "sha512-It4dO0K5v//JtTXuPkfEOaI3uUN87iYPnqo/ZzqCoG3g8uhA66QUMs/SrM0YK7/NAu+r4LMh/9dq2A7k+rHs+w==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, "node_modules/entities": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.3.tgz", @@ -14635,31 +14712,31 @@ } }, "node_modules/expo": { - "version": "56.0.8", - "resolved": "https://registry.npmjs.org/expo/-/expo-56.0.8.tgz", - "integrity": "sha512-GzQi5450yrCk5JRSlm0epsmtURBErh0wS77uWLZImFdnPICuX912MaRWooR+Q1Sw/7aQjp9F+KXH+dvrqGxpeQ==", + "version": "56.0.12", + "resolved": "https://registry.npmjs.org/expo/-/expo-56.0.12.tgz", + "integrity": "sha512-FxgdI/Yqva6iJOThZIHfvxlKPxs4EC4uScUnEswwSArR/Fj9k430O13R590LcOQTsdNsjIs+GBHwjfoAY6vmAQ==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.20.0", - "@expo/cli": "^56.1.13", + "@expo/cli": "^56.1.16", "@expo/config": "~56.0.9", - "@expo/config-plugins": "~56.0.8", + "@expo/config-plugins": "~56.0.9", "@expo/devtools": "~56.0.2", "@expo/dom-webview": "~56.0.5", - "@expo/fingerprint": "^0.19.3", + "@expo/fingerprint": "^0.19.4", "@expo/local-build-cache-provider": "^56.0.8", - "@expo/log-box": "^56.0.12", + "@expo/log-box": "^56.0.13", "@expo/metro": "~56.0.0", - "@expo/metro-config": "~56.0.13", + "@expo/metro-config": "~56.0.14", "@ungap/structured-clone": "^1.3.0", - "babel-preset-expo": "~56.0.14", - "expo-asset": "~56.0.15", - "expo-constants": "~56.0.16", - "expo-file-system": "~56.0.7", - "expo-font": "~56.0.5", + "babel-preset-expo": "~56.0.15", + "expo-asset": "~56.0.17", + "expo-constants": "~56.0.18", + "expo-file-system": "~56.0.8", + "expo-font": "~56.0.7", "expo-keep-awake": "~56.0.3", - "expo-modules-autolinking": "~56.0.14", - "expo-modules-core": "~56.0.14", + "expo-modules-autolinking": "~56.0.16", + "expo-modules-core": "~56.0.17", "pretty-format": "^29.7.0", "react-refresh": "^0.14.2", "whatwg-url-minimum": "^0.1.2" @@ -14704,13 +14781,13 @@ } }, "node_modules/expo-asset": { - "version": "56.0.15", - "resolved": "https://registry.npmjs.org/expo-asset/-/expo-asset-56.0.15.tgz", - "integrity": "sha512-BHGi2IAOPQTcOelkUdcz1WIknfCTRjkcpYHX1azjMwgYenrVC+J5qcqJGaC8eUOWLCRtkRJWGnmFQRYtLU1nUQ==", + "version": "56.0.17", + "resolved": "https://registry.npmjs.org/expo-asset/-/expo-asset-56.0.17.tgz", + "integrity": "sha512-GFN5j+8SPkyv0nfsiFHewmdB/D0tL237TsBE/gSfFOFy/J3a52py7IulcSqkA3sQE/u/UlD5BmvP5ssS4//nUg==", "license": "MIT", "dependencies": { "@expo/image-utils": "^0.10.1", - "expo-constants": "~56.0.16" + "expo-constants": "~56.0.18" }, "peerDependencies": { "expo": "*", @@ -14718,20 +14795,6 @@ "react-native": "*" } }, - "node_modules/expo-asset/node_modules/@babel/code-frame": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", - "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.29.7", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/expo-asset/node_modules/@expo/env": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@expo/env/-/env-2.3.0.tgz", @@ -14746,44 +14809,10 @@ "node": ">=20.12.0" } }, - "node_modules/expo-asset/node_modules/@expo/image-utils": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@expo/image-utils/-/image-utils-0.10.1.tgz", - "integrity": "sha512-YDeefvmYdihS7Wp3ESDUVnOgOSWmj2Cczm9lVNDdm4MqQLdAKm/LPYg83HtFQPfefRlAxyHrQR/O9kIXN9C1Wg==", - "license": "MIT", - "dependencies": { - "@expo/require-utils": "^56.1.3", - "@expo/spawn-async": "^1.8.0", - "chalk": "^4.0.0", - "getenv": "^2.0.0", - "jimp-compact": "0.16.1", - "parse-png": "^2.1.0", - "semver": "^7.6.0" - } - }, - "node_modules/expo-asset/node_modules/@expo/require-utils": { - "version": "56.1.3", - "resolved": "https://registry.npmjs.org/@expo/require-utils/-/require-utils-56.1.3.tgz", - "integrity": "sha512-KyLeOn/zzQSvuPpV5YhB/FPKnpQytno4luN918bGdPDssLBoS3N/0UbC3W0rJAn9kSFu+XpfR81eABRVsSdfgQ==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.20.0", - "@babel/core": "^7.25.2", - "@babel/plugin-transform-modules-commonjs": "^7.24.8" - }, - "peerDependencies": { - "typescript": "^5.0.0 || ^5.0.0-0 || ^6.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, "node_modules/expo-asset/node_modules/expo-constants": { - "version": "56.0.16", - "resolved": "https://registry.npmjs.org/expo-constants/-/expo-constants-56.0.16.tgz", - "integrity": "sha512-6tsiN+gmTUPp/atyA+uY9Tg8VOdXdmb4s/3TVGolfn6A/oCAraw1pcPZX5XllyD+xUguxB6eBSFAT8494hZVMA==", + "version": "56.0.18", + "resolved": "https://registry.npmjs.org/expo-constants/-/expo-constants-56.0.18.tgz", + "integrity": "sha512-8AMtbDGl/WVPnWlmbpGmvcdnNCy9E4PFnwdVwj600vljkMDPSxcAcjw8GVXEPk3PpZ+ngTqsrkltWyj0UKYAxw==", "license": "MIT", "dependencies": { "@expo/env": "~2.3.0" @@ -14859,16 +14888,6 @@ "react-native": "*" } }, - "node_modules/expo-crypto": { - "version": "56.0.4", - "resolved": "https://registry.npmjs.org/expo-crypto/-/expo-crypto-56.0.4.tgz", - "integrity": "sha512-fRNEhoXRXgAWBpe3/hq5X+KXTit3OZqdiAGts1YvNEUHQb+H5591mpPac0Yw+sZg9pXcrjRnzo5AxvZaENpc7g==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "expo": "*" - } - }, "node_modules/expo-document-picker": { "version": "56.0.4", "resolved": "https://registry.npmjs.org/expo-document-picker/-/expo-document-picker-56.0.4.tgz", @@ -14885,9 +14904,9 @@ "license": "MIT" }, "node_modules/expo-file-system": { - "version": "56.0.7", - "resolved": "https://registry.npmjs.org/expo-file-system/-/expo-file-system-56.0.7.tgz", - "integrity": "sha512-dcKzo8ShPloM7jgfnMcJStgQebhP8owVjCkNI/aX6NMFV1CYB8bxKGMdnzJ3mXk5nfaiW+F/lSKr2UIJ02WAUA==", + "version": "56.0.8", + "resolved": "https://registry.npmjs.org/expo-file-system/-/expo-file-system-56.0.8.tgz", + "integrity": "sha512-NrH41/8snGIBSbYicwVLB4txPdgCATd7ZYhMAGS3YJZ9GbnduhlAoV4/YCbGayjrbpE9bJb/6wegPL/zmvRMnQ==", "license": "MIT", "peerDependencies": { "expo": "*", @@ -14895,9 +14914,9 @@ } }, "node_modules/expo-font": { - "version": "56.0.5", - "resolved": "https://registry.npmjs.org/expo-font/-/expo-font-56.0.5.tgz", - "integrity": "sha512-WLoDu9hlEgPRKXJRR01HFLJ6Z2tFcORX/WFPRYBndmYc5kjQrFGH/j4BRaF3aBRPyYEAUXiUJybNLXkKCwEXQw==", + "version": "56.0.7", + "resolved": "https://registry.npmjs.org/expo-font/-/expo-font-56.0.7.tgz", + "integrity": "sha512-hpU/vRwPzsby9lPGkA4blDqLIIXYzoWnCZHr6PxvcWbY/uPObAiyhh6q+e0WYsB65SthK+PLH95jEnVag7fwEg==", "license": "MIT", "dependencies": { "fontfaceobserver": "^2.1.0" @@ -15037,54 +15056,6 @@ "expo": "*" } }, - "node_modules/expo-location/node_modules/@babel/code-frame": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", - "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.29.7", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/expo-location/node_modules/@expo/image-utils": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@expo/image-utils/-/image-utils-0.10.1.tgz", - "integrity": "sha512-YDeefvmYdihS7Wp3ESDUVnOgOSWmj2Cczm9lVNDdm4MqQLdAKm/LPYg83HtFQPfefRlAxyHrQR/O9kIXN9C1Wg==", - "license": "MIT", - "dependencies": { - "@expo/require-utils": "^56.1.3", - "@expo/spawn-async": "^1.8.0", - "chalk": "^4.0.0", - "getenv": "^2.0.0", - "jimp-compact": "0.16.1", - "parse-png": "^2.1.0", - "semver": "^7.6.0" - } - }, - "node_modules/expo-location/node_modules/@expo/require-utils": { - "version": "56.1.3", - "resolved": "https://registry.npmjs.org/@expo/require-utils/-/require-utils-56.1.3.tgz", - "integrity": "sha512-KyLeOn/zzQSvuPpV5YhB/FPKnpQytno4luN918bGdPDssLBoS3N/0UbC3W0rJAn9kSFu+XpfR81eABRVsSdfgQ==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.20.0", - "@babel/core": "^7.25.2", - "@babel/plugin-transform-modules-commonjs": "^7.24.8" - }, - "peerDependencies": { - "typescript": "^5.0.0 || ^5.0.0-0 || ^6.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, "node_modules/expo-manifests": { "version": "0.15.8", "resolved": "https://registry.npmjs.org/expo-manifests/-/expo-manifests-0.15.8.tgz", @@ -15293,9 +15264,9 @@ } }, "node_modules/expo-modules-autolinking": { - "version": "56.0.14", - "resolved": "https://registry.npmjs.org/expo-modules-autolinking/-/expo-modules-autolinking-56.0.14.tgz", - "integrity": "sha512-9ugtZkheNPYDkW4DZopY1rH2BCbUICaafUEPxRgbLDR5UNRF5K3cdHMIMEt8pxZPq2+eX4wCm+6pbSvdY/DPHg==", + "version": "56.0.16", + "resolved": "https://registry.npmjs.org/expo-modules-autolinking/-/expo-modules-autolinking-56.0.16.tgz", + "integrity": "sha512-9JnL4N46P8ubDpDIfWolDn7nxU2j1rY67xY/dNVuyH0m+HG+r/JI16VYtjIf4COpZtEuFo4D3h3MBeFzGucMnw==", "license": "MIT", "dependencies": { "@expo/require-utils": "^56.1.3", @@ -15307,43 +15278,31 @@ "expo-modules-autolinking": "bin/expo-modules-autolinking.js" } }, - "node_modules/expo-modules-autolinking/node_modules/@babel/code-frame": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", - "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "node_modules/expo-modules-core": { + "version": "56.0.17", + "resolved": "https://registry.npmjs.org/expo-modules-core/-/expo-modules-core-56.0.17.tgz", + "integrity": "sha512-5J8whnT7Ccp+BrFClLmpF76omBqn95VZExroTm01Dgjm4vpty1Rb7U3we+ZUceNHtRd07Lw30u7FNfDgIhEbRQ==", "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.29.7", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/expo-modules-autolinking/node_modules/@expo/require-utils": { - "version": "56.1.3", - "resolved": "https://registry.npmjs.org/@expo/require-utils/-/require-utils-56.1.3.tgz", - "integrity": "sha512-KyLeOn/zzQSvuPpV5YhB/FPKnpQytno4luN918bGdPDssLBoS3N/0UbC3W0rJAn9kSFu+XpfR81eABRVsSdfgQ==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.20.0", - "@babel/core": "^7.25.2", - "@babel/plugin-transform-modules-commonjs": "^7.24.8" + "@expo/expo-modules-macros-plugin": "0.2.2", + "expo-modules-jsi": "~56.0.10", + "invariant": "^2.2.4" }, "peerDependencies": { - "typescript": "^5.0.0 || ^5.0.0-0 || ^6.0.0" + "react": "*", + "react-native": "*", + "react-native-worklets": "^0.7.4 || ^0.8.0" }, "peerDependenciesMeta": { - "typescript": { + "react-native-worklets": { "optional": true } } }, "node_modules/expo-modules-jsi": { - "version": "56.0.7", - "resolved": "https://registry.npmjs.org/expo-modules-jsi/-/expo-modules-jsi-56.0.7.tgz", - "integrity": "sha512-iBAj4Xeh/8HT201VVxFlmf+VBfmtQV1ZUoJdLQQENm0+j9gnD2QswZLJyNo3CmNNXl46esJeLR5lpGpYZts/zA==", + "version": "56.0.10", + "resolved": "https://registry.npmjs.org/expo-modules-jsi/-/expo-modules-jsi-56.0.10.tgz", + "integrity": "sha512-fHZcFpYO/o62GYa6fJyAQJZcAShzhoN0iMMDzbr7vD3ewET6e1vAlTonbEakN9F0VHEgBFJ4NREy87uwVcpCuA==", "license": "MIT", "peerDependencies": { "react-native": "*" @@ -15550,33 +15509,23 @@ "react": "*" } }, - "node_modules/expo-secure-store": { - "version": "56.0.4", - "resolved": "https://registry.npmjs.org/expo-secure-store/-/expo-secure-store-56.0.4.tgz", - "integrity": "sha512-hjEi/gmpdFFJ9lYbdp3k3p/WchV7Gi0Qt8jt/m/0WJadqQrskafHAlDxbZkII1cN3Yd7zp9Lvkeq3UfGhSwirQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "expo": "*" - } - }, "node_modules/expo-server": { - "version": "56.0.4", - "resolved": "https://registry.npmjs.org/expo-server/-/expo-server-56.0.4.tgz", - "integrity": "sha512-4dJ57KuAwDl7eQGD6aG9kTzBIftWAfHH1+6Zxy7NcPCBrKYis3/H5enGUz1asH8HHhONXfJ5BdJqfEWAEAgWxA==", + "version": "56.0.5", + "resolved": "https://registry.npmjs.org/expo-server/-/expo-server-56.0.5.tgz", + "integrity": "sha512-SmM2p2g3Jrktpiazcst+OxhjSzOHXKAY4BPURHYHXvApzzoybMmrNF4IEZ8DKZ145BhSe4ydAmlEFCRTsdtgUQ==", "license": "MIT", "engines": { "node": ">=20.16.0" } }, "node_modules/expo-sharing": { - "version": "56.0.15", - "resolved": "https://registry.npmjs.org/expo-sharing/-/expo-sharing-56.0.15.tgz", - "integrity": "sha512-6Hy1+Mjy4UYXkFiDK3Ea934NUmA71i8dmZkDe+rrUHRzZAv4FR+q/VyiT7LzNFEqpT4wn4wcI66lc2QY526RsA==", + "version": "56.0.18", + "resolved": "https://registry.npmjs.org/expo-sharing/-/expo-sharing-56.0.18.tgz", + "integrity": "sha512-45w4BWNFmdTczp+fJX6YfwJrn9sX+VeRWz2VWLhauygcCrym44HtVDXX5yVYPB9TW9ZesLcEI+CCrCBNWL7smQ==", "license": "MIT", "dependencies": { - "@expo/config-plugins": "^56.0.8", - "@expo/config-types": "^56.0.5", + "@expo/config-plugins": "^56.0.9", + "@expo/config-types": "^56.0.6", "@expo/plist": "^0.7.0" }, "peerDependencies": { @@ -15600,12 +15549,12 @@ } }, "node_modules/expo-sharing/node_modules/@expo/config-plugins": { - "version": "56.0.8", - "resolved": "https://registry.npmjs.org/@expo/config-plugins/-/config-plugins-56.0.8.tgz", - "integrity": "sha512-phTuyBhgVLfqUHMjQkAfRtbyoY6yTxoKja1awtpVnEkoJDxPJuXx1KX5uvq1eZtt4bJQ08OBJ6P95INqRSHpRg==", + "version": "56.0.9", + "resolved": "https://registry.npmjs.org/@expo/config-plugins/-/config-plugins-56.0.9.tgz", + "integrity": "sha512-/6a/S9USwx8OC9tGjHxbviLFiBHyueN3aoNWMLvWDEJoZ1CIVW800ZBzwXq/FYNK2qzcN1LxFmQtzD1zeFQKNA==", "license": "MIT", "dependencies": { - "@expo/config-types": "^56.0.5", + "@expo/config-types": "^56.0.6", "@expo/json-file": "~10.2.0", "@expo/plist": "^0.7.0", "@expo/require-utils": "^56.1.3", @@ -15621,9 +15570,9 @@ } }, "node_modules/expo-sharing/node_modules/@expo/config-types": { - "version": "56.0.5", - "resolved": "https://registry.npmjs.org/@expo/config-types/-/config-types-56.0.5.tgz", - "integrity": "sha512-GsAHO/MwW9ZRdgnmyfRXqVGLCP/zejD6rWnp5OROp8mBGRObKm4HfrjlUyT1skjMwCj1OrURx9ZfIc6yeBAkIA==", + "version": "56.0.6", + "resolved": "https://registry.npmjs.org/@expo/config-types/-/config-types-56.0.6.tgz", + "integrity": "sha512-4Y6Aum5J4Re5NnxGVofRNe1aDwUBOmWhQYkynZsqzRtX/zEA1ADUeyHXuEckv9YD9djiyT7bKtLt5gKL3mA6VQ==", "license": "MIT" }, "node_modules/expo-sharing/node_modules/@expo/json-file": { @@ -15647,47 +15596,28 @@ "xmlbuilder": "^15.1.1" } }, - "node_modules/expo-sharing/node_modules/@expo/require-utils": { - "version": "56.1.3", - "resolved": "https://registry.npmjs.org/@expo/require-utils/-/require-utils-56.1.3.tgz", - "integrity": "sha512-KyLeOn/zzQSvuPpV5YhB/FPKnpQytno4luN918bGdPDssLBoS3N/0UbC3W0rJAn9kSFu+XpfR81eABRVsSdfgQ==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.20.0", - "@babel/core": "^7.25.2", - "@babel/plugin-transform-modules-commonjs": "^7.24.8" - }, - "peerDependencies": { - "typescript": "^5.0.0 || ^5.0.0-0 || ^6.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, "node_modules/expo-splash-screen": { - "version": "55.0.19", - "resolved": "https://registry.npmjs.org/expo-splash-screen/-/expo-splash-screen-55.0.19.tgz", - "integrity": "sha512-l8BWI/inLJW46Ojz5NgwvaM8LftrdXeFfZBUXhAoZxg44Qo2xKY76s0S1h3WIxWXT4sRKwK8YQzGr4k+zHubxQ==", + "version": "55.0.22", + "resolved": "https://registry.npmjs.org/expo-splash-screen/-/expo-splash-screen-55.0.22.tgz", + "integrity": "sha512-uvGybuDlYjVa33eTTeZlwEL6hZagRv64v+OxQVRuKOKZXImatxdE6ZGwHUXz/JxZkntZKYD8p9hBIjlupNLpAQ==", "license": "MIT", "dependencies": { - "@expo/prebuild-config": "^55.0.16" + "@expo/prebuild-config": "^55.0.19" }, "peerDependencies": { "expo": "*" } }, "node_modules/expo-splash-screen/node_modules/@expo/config": { - "version": "55.0.15", - "resolved": "https://registry.npmjs.org/@expo/config/-/config-55.0.15.tgz", - "integrity": "sha512-lHc0ELIQ8126jYOMZpLv3WIuvordW98jFg5aT/J1/12n2ycuXu01XLZkJsdw0avO34cusUYb1It+MvY8JiMduA==", + "version": "55.0.18", + "resolved": "https://registry.npmjs.org/@expo/config/-/config-55.0.18.tgz", + "integrity": "sha512-yezuyUk1VvVuwVyvKQrMHGosoC0WTh7HFAycpISMyeinSUHDjEBGvHUSQxtQ/71MR4g9lPJkEhFKeRjGx7I/Yg==", "license": "MIT", "dependencies": { - "@expo/config-plugins": "~55.0.8", + "@expo/config-plugins": "~55.0.10", "@expo/config-types": "^55.0.5", - "@expo/json-file": "^10.0.13", - "@expo/require-utils": "^55.0.4", + "@expo/json-file": "^10.0.15", + "@expo/require-utils": "^55.0.5", "deepmerge": "^4.3.1", "getenv": "^2.0.0", "glob": "^13.0.0", @@ -15697,14 +15627,14 @@ } }, "node_modules/expo-splash-screen/node_modules/@expo/config-plugins": { - "version": "55.0.8", - "resolved": "https://registry.npmjs.org/@expo/config-plugins/-/config-plugins-55.0.8.tgz", - "integrity": "sha512-8WfWTRntTCcowfOS+tHdB0z98gKetTwktg4G5TWkCkXVa8Jt1NUnvzaaU4UHk2vbR2U4N84RyZJFizSwfF6C9g==", + "version": "55.0.10", + "resolved": "https://registry.npmjs.org/@expo/config-plugins/-/config-plugins-55.0.10.tgz", + "integrity": "sha512-1txnRnMLIO5lM/Of/VyvDkCwZap0YFvCyfSTIlUQamhwhx6Rh7r8TXfcIstaDYUQ7X6GTMkNxLXWbcYS6ZAFDw==", "license": "MIT", "dependencies": { "@expo/config-types": "^55.0.5", - "@expo/json-file": "~10.0.13", - "@expo/plist": "^0.5.2", + "@expo/json-file": "~10.0.15", + "@expo/plist": "^0.5.4", "@expo/sdk-runtime-versions": "^1.0.0", "chalk": "^4.1.2", "debug": "^4.3.5", @@ -15717,16 +15647,65 @@ "xml2js": "0.6.0" } }, + "node_modules/expo-splash-screen/node_modules/@expo/config-plugins/node_modules/@expo/json-file": { + "version": "10.0.16", + "resolved": "https://registry.npmjs.org/@expo/json-file/-/json-file-10.0.16.tgz", + "integrity": "sha512-fcVkWEj+hLuP2yt5W0aw6LmDRqSPWDLUSxOMcmFeV+algmIF59sQVKCwB9btjQLd4V6x9N0pISkQEkBubUHrCw==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "~7.10.4", + "json5": "^2.2.3" + } + }, "node_modules/expo-splash-screen/node_modules/@expo/config-types": { "version": "55.0.5", "resolved": "https://registry.npmjs.org/@expo/config-types/-/config-types-55.0.5.tgz", "integrity": "sha512-sCmSUZG4mZ/ySXvfyyBdhjivz8Q539X1NondwDdYG7s3SBsk+wsgPJzYsqgAG/P9+l0xWjUD2F+kQ1cAJ6NNLg==", "license": "MIT" }, + "node_modules/expo-splash-screen/node_modules/@expo/image-utils": { + "version": "0.8.14", + "resolved": "https://registry.npmjs.org/@expo/image-utils/-/image-utils-0.8.14.tgz", + "integrity": "sha512-5Sn+jG4Cw+shC2wDMXoqSAJnvERbiwzHn05FpWtD5IBflfTIs5gUmjzwiGVyjOdlMSQhgRrw/AymPbmO9h9mpQ==", + "license": "MIT", + "dependencies": { + "@expo/require-utils": "^55.0.5", + "@expo/spawn-async": "^1.7.2", + "chalk": "^4.0.0", + "getenv": "^2.0.0", + "jimp-compact": "0.16.1", + "parse-png": "^2.1.0", + "semver": "^7.6.0" + } + }, + "node_modules/expo-splash-screen/node_modules/@expo/json-file": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/@expo/json-file/-/json-file-10.2.0.tgz", + "integrity": "sha512-S6XzKe3R9GQeHiUPXc3xJjOv2VJhOEwFYf7xdC2z2cUqt3kZJ9mSO877sNQloVdnW/SUCtPY3bexlM7nwq+CAQ==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.20.0", + "json5": "^2.2.3" + } + }, + "node_modules/expo-splash-screen/node_modules/@expo/json-file/node_modules/@babel/code-frame": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/expo-splash-screen/node_modules/@expo/plist": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@expo/plist/-/plist-0.5.2.tgz", - "integrity": "sha512-o4xdVdBpe4aTl3sPMZ2u3fJH4iG1I768EIRk1xRZP+GaFI93MaR3JvoFibYqxeTmLQ1p1kNEVqylfUjezxx45g==", + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@expo/plist/-/plist-0.5.4.tgz", + "integrity": "sha512-Jqppj0FULNq6Zp5JtQrFICl8TtpMjwwUbxEcEC2T3z7m+TOrTQEHZXz3D3Ay7vhbmvD+VMgfWJ4ARclJXeN8Eg==", "license": "MIT", "dependencies": { "@xmldom/xmldom": "^0.8.8", @@ -15735,16 +15714,16 @@ } }, "node_modules/expo-splash-screen/node_modules/@expo/prebuild-config": { - "version": "55.0.16", - "resolved": "https://registry.npmjs.org/@expo/prebuild-config/-/prebuild-config-55.0.16.tgz", - "integrity": "sha512-o4EAVgDGk1lISirtMD8hciO2vyMp7cWlPdfTtjjd5AXSfODVYDIDhygXrfvVQHmJXAztVqPUTKJT+BYOsVkYGQ==", + "version": "55.0.19", + "resolved": "https://registry.npmjs.org/@expo/prebuild-config/-/prebuild-config-55.0.19.tgz", + "integrity": "sha512-rLGjUXW0EUoxu/PJUOC0Lz5PEs27DmBA9Es3t2xV2ftI3kGZJK6oNt1vVq5D/Rn6MhZ5SFxqd3kziyP8ELoN6g==", "license": "MIT", "dependencies": { - "@expo/config": "~55.0.15", - "@expo/config-plugins": "~55.0.8", + "@expo/config": "~55.0.18", + "@expo/config-plugins": "~55.0.10", "@expo/config-types": "^55.0.5", - "@expo/image-utils": "^0.8.13", - "@expo/json-file": "^10.0.13", + "@expo/image-utils": "^0.8.14", + "@expo/json-file": "^10.0.15", "@react-native/normalize-colors": "0.83.6", "debug": "^4.3.1", "resolve-from": "^5.0.0", @@ -15755,6 +15734,39 @@ "expo": "*" } }, + "node_modules/expo-splash-screen/node_modules/@expo/require-utils": { + "version": "55.0.5", + "resolved": "https://registry.npmjs.org/@expo/require-utils/-/require-utils-55.0.5.tgz", + "integrity": "sha512-U4K/CQ2VpXuwfNGsN+daKmYOt15hCP8v/pXaYH6eut7kdYZo6SfJ1yr67BIcJ+1Gzzs+QzTxswAZChKpXmceyw==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.20.0", + "@babel/core": "^7.25.2", + "@babel/plugin-transform-modules-commonjs": "^7.24.8" + }, + "peerDependencies": { + "typescript": "^5.0.0 || ^5.0.0-0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/expo-splash-screen/node_modules/@expo/require-utils/node_modules/@babel/code-frame": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/expo-splash-screen/node_modules/@react-native/normalize-colors": { "version": "0.83.6", "resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.83.6.tgz", @@ -16020,165 +16032,50 @@ "commander": "^4.0.0", "glob": "^10.3.10", "lines-and-columns": "^1.1.6", - "mz": "^2.7.0", - "pirates": "^4.0.1", - "ts-interface-checker": "^0.1.9" - }, - "bin": { - "sucrase": "bin/sucrase", - "sucrase-node": "bin/sucrase-node" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/expo-updates/node_modules/write-file-atomic": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", - "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", - "license": "ISC", - "dependencies": { - "graceful-fs": "^4.1.11", - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.2" - } - }, - "node_modules/expo-updates/node_modules/xmlbuilder": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-14.0.0.tgz", - "integrity": "sha512-ts+B2rSe4fIckR6iquDjsKbQFK2NlUk6iG5nf14mDEyldgoc2nEKZ3jZWMPTxGQwVgToSjt6VGIho1H8/fNFTg==", - "license": "MIT", - "engines": { - "node": ">=8.0" - } - }, - "node_modules/expo/node_modules/@babel/code-frame": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", - "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.29.7", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/expo/node_modules/@expo/cli": { - "version": "56.1.13", - "resolved": "https://registry.npmjs.org/@expo/cli/-/cli-56.1.13.tgz", - "integrity": "sha512-7n5VzlBr7TKW0BgWgpEopWy+v8buPhMvbSEsuXD+bI1YIJBopkfWAub0qTvlc357E8wWOvV5MJXYyoeRvoOjoQ==", - "license": "MIT", - "dependencies": { - "@expo/code-signing-certificates": "^0.0.6", - "@expo/config": "~56.0.9", - "@expo/config-plugins": "~56.0.8", - "@expo/devcert": "^1.2.1", - "@expo/env": "~2.3.0", - "@expo/image-utils": "^0.10.1", - "@expo/inline-modules": "^0.0.10", - "@expo/json-file": "^10.2.0", - "@expo/log-box": "^56.0.12", - "@expo/metro": "~56.0.0", - "@expo/metro-config": "~56.0.13", - "@expo/metro-file-map": "^56.0.3", - "@expo/osascript": "^2.6.0", - "@expo/package-manager": "^1.12.1", - "@expo/plist": "^0.7.0", - "@expo/prebuild-config": "^56.0.14", - "@expo/require-utils": "^56.1.3", - "@expo/router-server": "^56.0.12", - "@expo/schema-utils": "^56.0.0", - "@expo/spawn-async": "^1.8.0", - "@expo/ws-tunnel": "^1.0.1", - "@expo/xcpretty": "^4.4.4", - "@react-native/dev-middleware": "0.85.3", - "accepts": "^1.3.8", - "arg": "^5.0.2", - "bplist-creator": "0.1.0", - "bplist-parser": "^0.3.1", - "chalk": "^4.0.0", - "ci-info": "^3.3.0", - "compression": "^1.7.4", - "connect": "^3.7.0", - "debug": "^4.3.4", - "dnssd-advertise": "^1.1.4", - "expo-server": "^56.0.4", - "fetch-nodeshim": "^0.4.10", - "getenv": "^2.0.0", - "glob": "^13.0.0", - "lan-network": "^0.2.1", - "multitars": "^1.0.0", - "node-forge": "^1.3.3", - "npm-package-arg": "^11.0.0", - "ora": "^3.4.0", - "picomatch": "^4.0.4", - "pretty-format": "^29.7.0", - "progress": "^2.0.3", - "prompts": "^2.3.2", - "resolve-from": "^5.0.0", - "semver": "^7.6.0", - "send": "^0.19.0", - "slugify": "^1.3.4", - "stacktrace-parser": "^0.1.10", - "structured-headers": "^0.4.1", - "terminal-link": "^2.1.1", - "toqr": "^0.1.1", - "wrap-ansi": "^7.0.0", - "ws": "^8.12.1", - "zod": "^3.25.76" + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" }, "bin": { - "expo-internal": "main.js" - }, - "peerDependencies": { - "expo": "*", - "expo-router": "*", - "react-native": "*" + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" }, - "peerDependenciesMeta": { - "expo-router": { - "optional": true - }, - "react-native": { - "optional": true - } + "engines": { + "node": ">=16 || 14 >=14.17" } }, - "node_modules/expo/node_modules/@expo/cli/node_modules/@expo/router-server": { - "version": "56.0.12", - "resolved": "https://registry.npmjs.org/@expo/router-server/-/router-server-56.0.12.tgz", - "integrity": "sha512-RqKV2/Z8BH/z8l0ngSpG6//5xxJPaF5dTQvSfPQ0nrvCjikGMeIvyj3B9BeLnmZZhxb3gBtXqrj3irAoiIp2aQ==", + "node_modules/expo-updates/node_modules/write-file-atomic": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", + "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", + "license": "ISC", + "dependencies": { + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.2" + } + }, + "node_modules/expo-updates/node_modules/xmlbuilder": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-14.0.0.tgz", + "integrity": "sha512-ts+B2rSe4fIckR6iquDjsKbQFK2NlUk6iG5nf14mDEyldgoc2nEKZ3jZWMPTxGQwVgToSjt6VGIho1H8/fNFTg==", + "license": "MIT", + "engines": { + "node": ">=8.0" + } + }, + "node_modules/expo/node_modules/@babel/code-frame": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", "license": "MIT", "dependencies": { - "debug": "^4.3.4" - }, - "peerDependencies": { - "@expo/metro-runtime": "^56.0.13", - "expo": "*", - "expo-constants": "^56.0.16", - "expo-font": "^56.0.5", - "expo-router": "*", - "expo-server": "^56.0.4", - "react": "*", - "react-dom": "*", - "react-server-dom-webpack": "~19.0.1 || ~19.1.2 || ~19.2.1" + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" }, - "peerDependenciesMeta": { - "@expo/metro-runtime": { - "optional": true - }, - "expo-router": { - "optional": true - }, - "react-dom": { - "optional": true - }, - "react-server-dom-webpack": { - "optional": true - } + "engines": { + "node": ">=6.9.0" } }, "node_modules/expo/node_modules/@expo/config": { @@ -16200,12 +16097,12 @@ } }, "node_modules/expo/node_modules/@expo/config-plugins": { - "version": "56.0.8", - "resolved": "https://registry.npmjs.org/@expo/config-plugins/-/config-plugins-56.0.8.tgz", - "integrity": "sha512-phTuyBhgVLfqUHMjQkAfRtbyoY6yTxoKja1awtpVnEkoJDxPJuXx1KX5uvq1eZtt4bJQ08OBJ6P95INqRSHpRg==", + "version": "56.0.9", + "resolved": "https://registry.npmjs.org/@expo/config-plugins/-/config-plugins-56.0.9.tgz", + "integrity": "sha512-/6a/S9USwx8OC9tGjHxbviLFiBHyueN3aoNWMLvWDEJoZ1CIVW800ZBzwXq/FYNK2qzcN1LxFmQtzD1zeFQKNA==", "license": "MIT", "dependencies": { - "@expo/config-types": "^56.0.5", + "@expo/config-types": "^56.0.6", "@expo/json-file": "~10.2.0", "@expo/plist": "^0.7.0", "@expo/require-utils": "^56.1.3", @@ -16221,9 +16118,9 @@ } }, "node_modules/expo/node_modules/@expo/config-types": { - "version": "56.0.5", - "resolved": "https://registry.npmjs.org/@expo/config-types/-/config-types-56.0.5.tgz", - "integrity": "sha512-GsAHO/MwW9ZRdgnmyfRXqVGLCP/zejD6rWnp5OROp8mBGRObKm4HfrjlUyT1skjMwCj1OrURx9ZfIc6yeBAkIA==", + "version": "56.0.6", + "resolved": "https://registry.npmjs.org/@expo/config-types/-/config-types-56.0.6.tgz", + "integrity": "sha512-4Y6Aum5J4Re5NnxGVofRNe1aDwUBOmWhQYkynZsqzRtX/zEA1ADUeyHXuEckv9YD9djiyT7bKtLt5gKL3mA6VQ==", "license": "MIT" }, "node_modules/expo/node_modules/@expo/env": { @@ -16240,21 +16137,6 @@ "node": ">=20.12.0" } }, - "node_modules/expo/node_modules/@expo/image-utils": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@expo/image-utils/-/image-utils-0.10.1.tgz", - "integrity": "sha512-YDeefvmYdihS7Wp3ESDUVnOgOSWmj2Cczm9lVNDdm4MqQLdAKm/LPYg83HtFQPfefRlAxyHrQR/O9kIXN9C1Wg==", - "license": "MIT", - "dependencies": { - "@expo/require-utils": "^56.1.3", - "@expo/spawn-async": "^1.8.0", - "chalk": "^4.0.0", - "getenv": "^2.0.0", - "jimp-compact": "0.16.1", - "parse-png": "^2.1.0", - "semver": "^7.6.0" - } - }, "node_modules/expo/node_modules/@expo/json-file": { "version": "10.2.0", "resolved": "https://registry.npmjs.org/@expo/json-file/-/json-file-10.2.0.tgz", @@ -16276,42 +16158,10 @@ "xmlbuilder": "^15.1.1" } }, - "node_modules/expo/node_modules/@expo/require-utils": { - "version": "56.1.3", - "resolved": "https://registry.npmjs.org/@expo/require-utils/-/require-utils-56.1.3.tgz", - "integrity": "sha512-KyLeOn/zzQSvuPpV5YhB/FPKnpQytno4luN918bGdPDssLBoS3N/0UbC3W0rJAn9kSFu+XpfR81eABRVsSdfgQ==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.20.0", - "@babel/core": "^7.25.2", - "@babel/plugin-transform-modules-commonjs": "^7.24.8" - }, - "peerDependencies": { - "typescript": "^5.0.0 || ^5.0.0-0 || ^6.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/expo/node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "license": "MIT", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/expo/node_modules/expo-constants": { - "version": "56.0.16", - "resolved": "https://registry.npmjs.org/expo-constants/-/expo-constants-56.0.16.tgz", - "integrity": "sha512-6tsiN+gmTUPp/atyA+uY9Tg8VOdXdmb4s/3TVGolfn6A/oCAraw1pcPZX5XllyD+xUguxB6eBSFAT8494hZVMA==", + "version": "56.0.18", + "resolved": "https://registry.npmjs.org/expo-constants/-/expo-constants-56.0.18.tgz", + "integrity": "sha512-8AMtbDGl/WVPnWlmbpGmvcdnNCy9E4PFnwdVwj600vljkMDPSxcAcjw8GVXEPk3PpZ+ngTqsrkltWyj0UKYAxw==", "license": "MIT", "dependencies": { "@expo/env": "~2.3.0" @@ -16321,36 +16171,6 @@ "react-native": "*" } }, - "node_modules/expo/node_modules/expo-modules-core": { - "version": "56.0.14", - "resolved": "https://registry.npmjs.org/expo-modules-core/-/expo-modules-core-56.0.14.tgz", - "integrity": "sha512-dl1TlYRm1k7xk9QeAyDoMfFE2p6rNyzHUcH5ArcGwUzO8YKku+Z2tQ8+kG7zLe3OhfMoJcFR/czrFy7vGSVI6w==", - "license": "MIT", - "dependencies": { - "@expo/expo-modules-macros-plugin": "~0.0.9", - "expo-modules-jsi": "~56.0.7", - "invariant": "^2.2.4" - }, - "peerDependencies": { - "react": "*", - "react-native": "*", - "react-native-worklets": "^0.7.4 || ^0.8.0" - }, - "peerDependenciesMeta": { - "react-native-worklets": { - "optional": true - } - } - }, - "node_modules/expo/node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/exponential-backoff": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.3.tgz", @@ -16401,10 +16221,12 @@ } }, "node_modules/express-rate-limit": { - "version": "8.4.0", + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.5.2.tgz", + "integrity": "sha512-5Kb34ipNX694DH48vN9irak1Qx30nb0PLYHXfJgw4YEjiC3ZEmZJhwOp+VfiCYwFzvFTdB9QkArYS5kXa2cx2A==", "license": "MIT", "dependencies": { - "ip-address": "10.1.0" + "ip-address": "^10.2.0" }, "engines": { "node": ">= 16" @@ -16599,7 +16421,9 @@ } }, "node_modules/fast-uri": { - "version": "3.1.0", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", + "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", "dev": true, "funding": [ { @@ -16861,7 +16685,9 @@ "license": "MIT" }, "node_modules/follow-redirects": { - "version": "1.15.11", + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", "funding": [ { "type": "individual", @@ -16948,14 +16774,16 @@ } }, "node_modules/form-data": { - "version": "4.0.5", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz", + "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==", "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", - "hasown": "^2.0.2", - "mime-types": "^2.1.12" + "hasown": "^2.0.4", + "mime-types": "^2.1.35" }, "engines": { "node": ">= 6" @@ -17221,10 +17049,12 @@ } }, "node_modules/glob/node_modules/minimatch": { - "version": "10.2.2", + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^5.0.2" + "brace-expansion": "^5.0.5" }, "engines": { "node": "18 || 20 || >=22" @@ -17512,7 +17342,9 @@ } }, "node_modules/hasown": { - "version": "2.0.2", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -17579,7 +17411,9 @@ "license": "MIT" }, "node_modules/hono": { - "version": "4.12.14", + "version": "4.12.27", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.27.tgz", + "integrity": "sha512-1yrb/+w6HWQJrUCLkJ2IF5jNIPvvFkblV5RNOYl6bV+OA6p9GLcMpHFFGTosSvHvcAUibuUukRqhlYI4z32C7Q==", "dev": true, "license": "MIT", "engines": { @@ -17930,7 +17764,9 @@ } }, "node_modules/ip-address": { - "version": "10.1.0", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.2.0.tgz", + "integrity": "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==", "license": "MIT", "engines": { "node": ">= 12" @@ -18773,7 +18609,9 @@ "license": "MIT" }, "node_modules/jest-config/node_modules/brace-expansion": { - "version": "1.1.12", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", + "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", "dev": true, "license": "MIT", "dependencies": { @@ -19121,7 +18959,9 @@ "license": "MIT" }, "node_modules/jest-runtime/node_modules/brace-expansion": { - "version": "1.1.12", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", + "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", "dev": true, "license": "MIT", "dependencies": { @@ -19205,7 +19045,9 @@ } }, "node_modules/jest-util/node_modules/picomatch": { - "version": "2.3.1", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "license": "MIT", "engines": { "node": ">=8.6" @@ -19306,7 +19148,19 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.1.1", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz", + "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], "license": "MIT", "dependencies": { "argparse": "^2.0.1" @@ -20959,7 +20813,9 @@ } }, "node_modules/micromatch/node_modules/picomatch": { - "version": "2.3.1", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "license": "MIT", "engines": { "node": ">=8.6" @@ -21035,10 +20891,12 @@ } }, "node_modules/minimatch": { - "version": "9.0.6", + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "license": "ISC", "dependencies": { - "brace-expansion": "^5.0.2" + "brace-expansion": "^2.0.2" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -21047,6 +20905,21 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/minimatch/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/minimatch/node_modules/brace-expansion": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", + "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, "node_modules/minimist": { "version": "1.2.8", "dev": true, @@ -21093,37 +20966,6 @@ "version": "2.1.3", "license": "MIT" }, - "node_modules/msgpackr": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-2.0.2.tgz", - "integrity": "sha512-c5hYOXFbP79Slh6Dzd2wzk+jnV7mX1UxfMYtilnY1NmalXPqG8DGb5cYCMBrW4AsH3zekBBZd4QrKz9NhtvYLQ==", - "license": "MIT", - "optionalDependencies": { - "msgpackr-extract": "^3.0.4" - } - }, - "node_modules/msgpackr-extract": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-3.0.4.tgz", - "integrity": "sha512-4kmO/MdyUIkLIvTPr8VHLil4AtoKIoniWPIEk5+CDy0xnWC84azhSFmuJ7PxZdsYtiP5kEeQsORAVIeMgxT+Hw==", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "dependencies": { - "node-gyp-build-optional-packages": "5.2.2" - }, - "bin": { - "download-msgpackr-prebuilds": "bin/download-prebuilds.js" - }, - "optionalDependencies": { - "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.4", - "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.4", - "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.4", - "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.4", - "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.4", - "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.4" - } - }, "node_modules/msw": { "version": "2.13.6", "dev": true, @@ -21207,9 +21049,9 @@ } }, "node_modules/multer": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/multer/-/multer-2.1.1.tgz", - "integrity": "sha512-mo+QTzKlx8R7E5ylSXxWzGoXoZbOsRMpyitcht8By2KHvMbf3tjwosZ/Mu/XYU6UuJ3VZnODIrak5ZrPiPyB6A==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/multer/-/multer-2.2.0.tgz", + "integrity": "sha512-6rdyFg2kLrMh9Jee7/BMPuV9lEAd7lLW2YUpF9/YxR7njyoUwwQ0ZPh3TaIY50Sw6vlyD2HW3wGOkTS4P79xrQ==", "license": "MIT", "dependencies": { "append-field": "^1.0.0", @@ -21388,21 +21230,6 @@ "node": ">= 6.13.0" } }, - "node_modules/node-gyp-build-optional-packages": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.2.2.tgz", - "integrity": "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==", - "license": "MIT", - "optional": true, - "dependencies": { - "detect-libc": "^2.0.1" - }, - "bin": { - "node-gyp-build-optional-packages": "bin.js", - "node-gyp-build-optional-packages-optional": "optional.js", - "node-gyp-build-optional-packages-test": "build-test.js" - } - }, "node_modules/node-int64": { "version": "0.4.0", "license": "MIT" @@ -22742,10 +22569,13 @@ } }, "node_modules/qs": { - "version": "6.15.1", + "version": "6.15.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.3.tgz", + "integrity": "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==", "license": "BSD-3-Clause", "dependencies": { - "side-channel": "^1.1.0" + "es-define-property": "^1.0.1", + "side-channel": "^1.1.1" }, "engines": { "node": ">=0.6" @@ -23187,9 +23017,9 @@ } }, "node_modules/react-native-worklets": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/react-native-worklets/-/react-native-worklets-0.10.0.tgz", - "integrity": "sha512-JhE6IxDf6iabC0qu3+TAKA4v9RlluXmoIngPQX7/QUByf75lfrsHZ6/dQhyjEWnp1EEQiwzz8Cpew140ZcewDw==", + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/react-native-worklets/-/react-native-worklets-0.9.2.tgz", + "integrity": "sha512-bCBV4dwcoLnqpk0bbL92nNuv6km37DRpamCX/nqNxsRs6LmLeFoFe/a7OKL1pVDi3PY97C0BEhmq8Qq/iNvpVg==", "dev": true, "license": "MIT", "dependencies": { @@ -23243,23 +23073,6 @@ } } }, - "node_modules/react-reconciler": { - "version": "0.33.0", - "resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.33.0.tgz", - "integrity": "sha512-KetWRytFv1epdpJc3J4G75I4WrplZE5jOL7Yq0p34+OVOKF4Se7WrdIdVC45XsSSmUTlht2FM/fM1FZb1mfQeA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "scheduler": "^0.27.0" - }, - "engines": { - "node": ">=0.10.0" - }, - "peerDependencies": { - "react": "^19.2.0" - } - }, "node_modules/react-refresh": { "version": "0.14.2", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", @@ -23698,12 +23511,14 @@ "license": "MIT" }, "node_modules/rolldown": { - "version": "1.0.0-rc.17", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.1.3.tgz", + "integrity": "sha512-1F1eEtUBtFvcGm1HQ9TiUIUHPQG7mSAODrhIzjxoUEFuo8OcbrGLiVLkevNgj84TE4lnHvnumwFjhJO5Eu135g==", "dev": true, "license": "MIT", "dependencies": { - "@oxc-project/types": "=0.127.0", - "@rolldown/pluginutils": "1.0.0-rc.17" + "@oxc-project/types": "=0.137.0", + "@rolldown/pluginutils": "^1.0.0" }, "bin": { "rolldown": "bin/cli.mjs" @@ -23712,21 +23527,21 @@ "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.0.0-rc.17", - "@rolldown/binding-darwin-arm64": "1.0.0-rc.17", - "@rolldown/binding-darwin-x64": "1.0.0-rc.17", - "@rolldown/binding-freebsd-x64": "1.0.0-rc.17", - "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.17", - "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.17", - "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.17", - "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.17", - "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.17", - "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.17", - "@rolldown/binding-linux-x64-musl": "1.0.0-rc.17", - "@rolldown/binding-openharmony-arm64": "1.0.0-rc.17", - "@rolldown/binding-wasm32-wasi": "1.0.0-rc.17", - "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.17", - "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.17" + "@rolldown/binding-android-arm64": "1.1.3", + "@rolldown/binding-darwin-arm64": "1.1.3", + "@rolldown/binding-darwin-x64": "1.1.3", + "@rolldown/binding-freebsd-x64": "1.1.3", + "@rolldown/binding-linux-arm-gnueabihf": "1.1.3", + "@rolldown/binding-linux-arm64-gnu": "1.1.3", + "@rolldown/binding-linux-arm64-musl": "1.1.3", + "@rolldown/binding-linux-ppc64-gnu": "1.1.3", + "@rolldown/binding-linux-s390x-gnu": "1.1.3", + "@rolldown/binding-linux-x64-gnu": "1.1.3", + "@rolldown/binding-linux-x64-musl": "1.1.3", + "@rolldown/binding-openharmony-arm64": "1.1.3", + "@rolldown/binding-wasm32-wasi": "1.1.3", + "@rolldown/binding-win32-arm64-msvc": "1.1.3", + "@rolldown/binding-win32-x64-msvc": "1.1.3" } }, "node_modules/router": { @@ -24159,12 +23974,14 @@ } }, "node_modules/side-channel": { - "version": "1.1.0", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", + "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", + "object-inspect": "^1.13.4", + "side-channel-list": "^1.0.1", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" }, @@ -24335,34 +24152,13 @@ } }, "node_modules/socket.io-adapter": { - "version": "2.5.7", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.7.tgz", - "integrity": "sha512-e0LyK91f3cUxTmv95/KzoLg47+zF+s/sbxRGDNsyG4dmIP8ZSX8ax6byOxfJXeNNtS/8AZlfD+uP7gBeR7DLlg==", + "version": "2.5.8", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.8.tgz", + "integrity": "sha512-6Oy52pbg+kvdCVvjcN+FnY7BvxZ7cIHNScbvztT/It5d0vbwoJoVZmF2gjJmnV0/4WlXRfG15zc45ySk9Ah8bw==", "license": "MIT", "dependencies": { "debug": "~4.4.1", - "ws": "~8.20.1" - } - }, - "node_modules/socket.io-adapter/node_modules/ws": { - "version": "8.20.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.1.tgz", - "integrity": "sha512-It4dO0K5v//JtTXuPkfEOaI3uUN87iYPnqo/ZzqCoG3g8uhA66QUMs/SrM0YK7/NAu+r4LMh/9dq2A7k+rHs+w==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } + "ws": "~8.21.0" } }, "node_modules/socket.io-client": { @@ -25307,7 +25103,9 @@ "license": "MIT" }, "node_modules/test-exclude/node_modules/brace-expansion": { - "version": "1.1.12", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", + "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", "dev": true, "license": "MIT", "dependencies": { @@ -25335,7 +25133,9 @@ } }, "node_modules/test-exclude/node_modules/minimatch": { - "version": "3.1.3", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", "dependencies": { @@ -25345,21 +25145,6 @@ "node": "*" } }, - "node_modules/test-renderer": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/test-renderer/-/test-renderer-1.2.0.tgz", - "integrity": "sha512-JYiEGbgBGtmHAWX8Kf99gGRL1HtjcRVHLrmJNTZL4vFs9XrnWcuL45Iszw/pO4094+JR7havSrN+ds6YTOpSQA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@types/react-reconciler": "~0.33.0", - "react-reconciler": "~0.33.0" - }, - "peerDependencies": { - "react": "^19.0.0" - } - }, "node_modules/text-decoder": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.7.tgz", @@ -25442,7 +25227,9 @@ } }, "node_modules/tinyglobby": { - "version": "0.2.16", + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", "license": "MIT", "dependencies": { "fdir": "^6.5.0", @@ -25968,9 +25755,9 @@ } }, "node_modules/undici": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/undici/-/undici-7.27.1.tgz", - "integrity": "sha512-UDdpiex+mzigiyrXrGbiUaF4HzTNhKbh2vRNFaTMzcqmLIPrZxaCtwo/1TMSuWoM1Xz3WiTo9KdgI3kRqYzJGg==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.28.0.tgz", + "integrity": "sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA==", "license": "MIT", "engines": { "node": ">=20.18.1" @@ -26240,15 +26027,17 @@ } }, "node_modules/vite": { - "version": "8.0.10", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.1.0.tgz", + "integrity": "sha512-BuJcQK/56NQTWDGn4ABea3q4SSBdNPWwNZKTkkUpcMPnLoquSYH8llRtSUIgoL1KSCpHt5eghLShn50mH36y7Q==", "dev": true, "license": "MIT", "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", - "postcss": "^8.5.10", - "rolldown": "1.0.0-rc.17", - "tinyglobby": "^0.2.16" + "postcss": "^8.5.15", + "rolldown": "~1.1.2", + "tinyglobby": "^0.2.17" }, "bin": { "vite": "bin/vite.js" @@ -26264,7 +26053,7 @@ }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", - "@vitejs/devtools": "^0.1.0", + "@vitejs/devtools": "^0.3.0", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", @@ -26850,7 +26639,9 @@ "license": "ISC" }, "node_modules/yaml": { - "version": "2.8.2", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", + "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", "license": "ISC", "bin": { "yaml": "bin.mjs" diff --git a/package.json b/package.json index 434bb49e..677f88a3 100644 --- a/package.json +++ b/package.json @@ -202,7 +202,7 @@ "lint-staged": "15.5.1", "msw": "^2.13.6", "prettier": "3.5.3", - "react-native-worklets": "^0.10.0", + "react-native-worklets": "^0.9.2", "react-test-renderer": "^19.2.7", "supertest": "^7.2.2", "ts-jest": "^29.4.6", diff --git a/src/navigation/AppNavigator.tsx b/src/navigation/AppNavigator.tsx index a27ec72f..b7daf745 100644 --- a/src/navigation/AppNavigator.tsx +++ b/src/navigation/AppNavigator.tsx @@ -2,52 +2,57 @@ import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; import { NavigationContainer, type LinkingOptions } from '@react-navigation/native'; import { createNativeStackNavigator } from '@react-navigation/native-stack'; import * as Notifications from 'expo-notifications'; -import React from 'react'; -import { StatusBar, Text } from 'react-native'; +import React, { Suspense } from 'react'; +import { ActivityIndicator, StatusBar, Text, View } from 'react-native'; import { useNavigationTheme } from '../theme'; import type { RootStackParamList, MainTabParamList, PetStackParamList } from './types'; import { DEEP_LINK_PREFIX } from './types'; +import LazyScreen from '../components/LazyScreen'; import { useNotificationBadge } from '../hooks/useNotificationBadge'; import type { Pet } from '../models/Pet'; -import AdoptionScreen from '../screens/AdoptionScreen'; -import AppointmentScreen from '../screens/AppointmentScreen'; -import AuditHistoryScreen from '../screens/AuditHistoryScreen'; +// ── Critical screens (eagerly loaded) ──────────────────────────────────────── import AuthNavigator from '../screens/AuthNavigator'; -import ClinicalNotesScreen from '../screens/ClinicalNotesScreen'; -import CommunityScreen from '../screens/CommunityScreen'; -import DeleteAccountScreen from '../screens/DeleteAccountScreen'; -import EmergencyContactsScreen from '../screens/EmergencyContactsScreen'; -import FiatOnRampScreen from '../screens/FiatOnRampScreen'; -import ForumScreen from '../screens/ForumScreen'; -import HealthAlertsScreen from '../screens/HealthAlertsScreen'; -import LostFoundScreen from '../screens/LostFoundScreen'; -import ManualEntryScreen from '../screens/ManualEntryScreen'; -import MedicalRecordSearchScreen from '../screens/MedicalRecordSearchScreen'; -import MedicalRecordViewerScreen from '../screens/MedicalRecordViewerScreen'; -import MedicationScreen from '../screens/MedicationScreen'; -import NearbyVetScreen from '../screens/NearbyVetScreen'; -import NotificationCenterScreen from '../screens/NotificationCenterScreen'; -import NotificationPreferencesScreen from '../screens/NotificationPreferencesScreen'; import OnboardingScreen from '../screens/OnboardingScreen'; -import PaymentScreen from '../screens/PaymentScreen'; -import PetDetailScreen from '../screens/PetDetailScreen'; -import PetFormScreen from '../screens/PetFormScreen'; import PetHealthDashboardScreen from '../screens/PetHealthDashboardScreen'; -import PetHealthMetricsScreen from '../screens/PetHealthMetricsScreen'; import PetListScreen from '../screens/PetListScreen'; -import PetProfileScreen from '../screens/PetProfileScreen'; -import PetShareScreen from '../screens/PetShareScreen'; -import PrivacyDashboardScreen from '../screens/PrivacyDashboardScreen'; -import ProfileScreen from '../screens/ProfileScreen'; -import QRScannerScreen from '../screens/QRScannerScreen'; -import ReconciliationScreen from '../screens/ReconciliationScreen'; -import ReferralScreen from '../screens/ReferralScreen'; -import TelemedicineScreen from '../screens/TelemedicineScreen'; -import TravelCertificateScreen from '../screens/TravelCertificateScreen'; -import TrustlineScreen from '../screens/TrustlineScreen'; -import VaccinationScreen from '../screens/VaccinationScreen'; -import VetMapScreen from '../screens/VetMapScreen'; +// ── Non-critical screens (lazy loaded) ─────────────────────────────────────── +const AdoptionScreen = React.lazy(() => import('../screens/AdoptionScreen')); +const AppointmentScreen = React.lazy(() => import('../screens/AppointmentScreen')); +const AuditHistoryScreen = React.lazy(() => import('../screens/AuditHistoryScreen')); +const ClinicalNotesScreen = React.lazy(() => import('../screens/ClinicalNotesScreen')); +const CommunityScreen = React.lazy(() => import('../screens/CommunityScreen')); +const DeleteAccountScreen = React.lazy(() => import('../screens/DeleteAccountScreen')); +const EmergencyContactsScreen = React.lazy(() => import('../screens/EmergencyContactsScreen')); +const FiatOnRampScreen = React.lazy(() => import('../screens/FiatOnRampScreen')); +const ForumScreen = React.lazy(() => import('../screens/ForumScreen')); +const HealthAlertsScreen = React.lazy(() => import('../screens/HealthAlertsScreen')); +const LostFoundScreen = React.lazy(() => import('../screens/LostFoundScreen')); +const ManualEntryScreen = React.lazy(() => import('../screens/ManualEntryScreen')); +const MedicalRecordSearchScreen = React.lazy(() => import('../screens/MedicalRecordSearchScreen')); +const MedicalRecordViewerScreen = React.lazy(() => import('../screens/MedicalRecordViewerScreen')); +const MedicationScreen = React.lazy(() => import('../screens/MedicationScreen')); +const NearbyVetScreen = React.lazy(() => import('../screens/NearbyVetScreen')); +const NotificationCenterScreen = React.lazy(() => import('../screens/NotificationCenterScreen')); +const NotificationPreferencesScreen = React.lazy( + () => import('../screens/NotificationPreferencesScreen'), +); +const PaymentScreen = React.lazy(() => import('../screens/PaymentScreen')); +const PetDetailScreen = React.lazy(() => import('../screens/PetDetailScreen')); +const PetFormScreen = React.lazy(() => import('../screens/PetFormScreen')); +const PetHealthMetricsScreen = React.lazy(() => import('../screens/PetHealthMetricsScreen')); +const PetProfileScreen = React.lazy(() => import('../screens/PetProfileScreen')); +const PetShareScreen = React.lazy(() => import('../screens/PetShareScreen')); +const PrivacyDashboardScreen = React.lazy(() => import('../screens/PrivacyDashboardScreen')); +const ProfileScreen = React.lazy(() => import('../screens/ProfileScreen')); +const QRScannerScreen = React.lazy(() => import('../screens/QRScannerScreen')); +const ReconciliationScreen = React.lazy(() => import('../screens/ReconciliationScreen')); +const ReferralScreen = React.lazy(() => import('../screens/ReferralScreen')); +const TelemedicineScreen = React.lazy(() => import('../screens/TelemedicineScreen')); +const TravelCertificateScreen = React.lazy(() => import('../screens/TravelCertificateScreen')); +const TrustlineScreen = React.lazy(() => import('../screens/TrustlineScreen')); +const VaccinationScreen = React.lazy(() => import('../screens/VaccinationScreen')); +const VetMapScreen = React.lazy(() => import('../screens/VetMapScreen')); import { extractDeepLinkParams } from '../services/notificationService'; import performance from '../utils/performance'; @@ -69,42 +74,52 @@ function PetNavigator() { )} - {() => } + {() => ( + + + + )} {({ route, navigation }) => ( - navigation.goBack()} - onEdit={(pet: Pet) => navigation.navigate('PetForm', { pet })} - onHealthDashboard={(petId, petName) => - navigation.navigate('PetHealthDashboard', { petId, petName }) - } - onShare={(petId, petName) => navigation.navigate('PetShare', { petId, petName })} - onAuditHistory={(petId, petName) => - navigation.navigate('AuditHistory', { - entityType: 'pet', - entityId: petId, - title: `${petName} • Audit`, - }) - } - onViewProfile={(petId) => navigation.navigate('PetProfile', { petId })} - /> + + navigation.goBack()} + onEdit={(pet: Pet) => navigation.navigate('PetForm', { pet })} + onHealthDashboard={(petId, petName) => + navigation.navigate('PetHealthDashboard', { petId, petName }) + } + onShare={(petId, petName) => navigation.navigate('PetShare', { petId, petName })} + onAuditHistory={(petId, petName) => + navigation.navigate('AuditHistory', { + entityType: 'pet', + entityId: petId, + title: `${petName} • Audit`, + }) + } + onViewProfile={(petId) => navigation.navigate('PetProfile', { petId })} + /> + )} {({ route, navigation }) => ( - navigation.goBack()} - /> + + navigation.goBack()} + /> + )} {({ route, navigation }) => ( - navigation.goBack()} /> + + navigation.goBack()} /> + )} @@ -124,60 +139,92 @@ function PetNavigator() { {({ route, navigation }) => ( - navigation.goBack()} - /> + + navigation.goBack()} + /> + )} {({ route, navigation }) => ( - navigation.goBack()} - onSaved={() => navigation.goBack()} - /> + + navigation.goBack()} + onSaved={() => navigation.goBack()} + /> + )} {({ route, navigation }) => ( - navigation.goBack()} - /> + + navigation.goBack()} + /> + )} {({ route, navigation }) => ( - navigation.goBack()} - /> + + navigation.goBack()} + /> + )} {({ route, navigation }) => ( - navigation.goBack()} - /> + + navigation.goBack()} + /> + )} {({ route, navigation }) => ( - navigation.goBack()} - /> + + navigation.goBack()} + /> + )} - {({ navigation }) => navigation.goBack()} />} + {({ navigation }) => ( + + navigation.goBack()} /> + + )} + + + {({ navigation }) => ( + + { + navigation.getParent()?.navigate('Appointments', { + initialVetName: vetName, + initialDate: date, + initialTime: time, + openBooking: true, + }); + }} + /> + + )} {({ navigation }) => ( @@ -194,42 +241,60 @@ function PetNavigator() { )} - {({ navigation }) => navigation.goBack()} />} + {({ navigation }) => ( + + navigation.goBack()} /> + + )} - {({ navigation }) => navigation.goBack()} />} + {({ navigation }) => ( + + navigation.goBack()} /> + + )} - {({ navigation }) => navigation.goBack()} />} + {({ navigation }) => ( + + navigation.goBack()} /> + + )} {({ navigation }) => ( - navigation.navigate('DeleteAccount')} /> + + navigation.navigate('DeleteAccount')} /> + )} {({ navigation }) => ( - navigation.goBack()} - onDeleted={() => - navigation - .getParent() - ?.getParent() - ?.reset({ index: 0, routes: [{ name: 'Auth' }] }) - } - /> + + navigation.goBack()} + onDeleted={() => + navigation + .getParent() + ?.getParent() + ?.reset({ index: 0, routes: [{ name: 'Auth' }] }) + } + /> + )} {({ route, navigation }) => ( - navigation.goBack()} - /> + + navigation.goBack()} + /> + )} @@ -254,41 +319,64 @@ function MainTabs() { component={PetNavigator} options={{ title: 'Pets', headerShown: false }} /> - - - - - - - - + + {() => ( + + + + )} + + + {() => ( + + + + )} + + + {() => ( + + + + )} + + + {() => ( + + + + )} + + + {() => ( + + + + )} + + + {() => ( + + + + )} + + + {() => ( + + + + )} + + + {() => ( + + + + )} + 0 ? badgeCount : undefined, @@ -299,12 +387,23 @@ function MainTabs() { }} listeners={{ tabPress: () => { - // Refresh badge when navigating away from Notifications tab refreshBadge(); }, }} - /> - + > + {() => ( + + + + )} + + + {() => ( + + + + )} + ); } @@ -424,95 +523,124 @@ export default function AppNavigator() { barStyle={navTheme.dark ? 'light-content' : 'dark-content'} backgroundColor={navTheme.colors.card} /> - } - theme={navTheme} - linking={linking} - onStateChange={() => { - const route = ( - navRef.current as { getCurrentRoute?: () => { name?: string } | undefined } | null - )?.getCurrentRoute?.(); - const name = route?.name; - // finish previous span - try { - performance.finishSpan(currentScreenSpan.current); - } catch (e) { - // ignore - } - - if (name) { - analyticsService.screenView(name); - // start new screen span - currentScreenSpan.current = performance.startSpan(`screen:${name}`); - performance.recordMetric('screen.render_start', Date.now(), { screen: name }); - } - }} + + + + } > - - - {({ navigation }) => ( - navigation.replace('Auth')} - onSkip={() => navigation.replace('Auth')} - /> - )} - - - - {({ navigation }) => ( - navigation.replace('Main')} /> - )} - - - - - + } + theme={navTheme} + linking={linking} + onStateChange={() => { + const route = ( + navRef.current as { getCurrentRoute?: () => { name?: string } | undefined } | null + )?.getCurrentRoute?.(); + const name = route?.name; + // finish previous span + try { + performance.finishSpan(currentScreenSpan.current); + } catch (e) { + // ignore + } - {/* Modals */} - - - {({ route, navigation }) => ( - { - if (route.params?.onScanSuccess) { - route.params.onScanSuccess(data); - } - navigation.goBack(); - }} - onClose={() => navigation.goBack()} - onManualEntry={() => navigation.replace('ManualEntry')} + if (name) { + analyticsService.screenView(name); + // start new screen span + currentScreenSpan.current = performance.startSpan(`screen:${name}`); + performance.recordMetric('screen.render_start', Date.now(), { screen: name }); + } + }} + > + + + {({ navigation }) => ( + navigation.replace('Auth')} + onSkip={() => navigation.replace('Auth')} /> )} - + + {({ navigation }) => ( - navigation.goBack()} - onClose={() => navigation.goBack()} - /> + navigation.replace('Main')} /> + )} + + + + + {() => ( + + + )} - - - - + name="LostFound" + options={{ headerShown: true, title: 'Lost & Found' }} + > + {() => ( + + + + )} + + + {/* Modals */} + + + {({ route, navigation }) => ( + + { + if (route.params?.onScanSuccess) { + route.params.onScanSuccess(data); + } + navigation.goBack(); + }} + onClose={() => navigation.goBack()} + onManualEntry={() => navigation.replace('ManualEntry')} + /> + + )} + + + {({ navigation }) => ( + + navigation.goBack()} + onClose={() => navigation.goBack()} + /> + + )} + + + {() => ( + + + + )} + + + {() => ( + + + + )} + + + + + ); } diff --git a/src/screens/AppointmentScreen.tsx b/src/screens/AppointmentScreen.tsx index e3550e16..dc60c110 100644 --- a/src/screens/AppointmentScreen.tsx +++ b/src/screens/AppointmentScreen.tsx @@ -1,5 +1,5 @@ -import { useRoute } from '@react-navigation/native'; import AsyncStorage from '@react-native-async-storage/async-storage'; +import { useRoute } from '@react-navigation/native'; import React, { useCallback, useEffect, useRef, useState } from 'react'; import { ActivityIndicator, @@ -561,7 +561,11 @@ const AppointmentScreen: React.FC = () => { setBookingVisible(true)} /> @@ -586,9 +590,7 @@ const AppointmentScreen: React.FC = () => { onPress={() => setShowArchived((v) => !v)} accessibilityLabel="Toggle show archived appointments" > - - {showArchived ? '☑' : '☐'} Show archived - + {showArchived ? '☑' : '☐'} Show archived {visibleRecent.map((item) => renderSwipeableItem(item))} diff --git a/src/screens/PetPhotosScreen.tsx b/src/screens/PetPhotosScreen.tsx index 344a1859..1569494a 100644 --- a/src/screens/PetPhotosScreen.tsx +++ b/src/screens/PetPhotosScreen.tsx @@ -10,7 +10,7 @@ * and photoService to handle strip/compress/upload/delete operations. */ -import React, { useCallback, useEffect, useState } from 'react'; +import React, { useCallback, useEffect, useRef, useState } from 'react'; import { ActivityIndicator, Alert, @@ -25,9 +25,12 @@ import { } from 'react-native'; import { launchImageLibrary } from 'react-native-image-picker'; +import { OptimizedImage } from '../components/OptimizedImage'; import photoService, { type PetPhoto, type PhotoQuality } from '../services/photoService'; import { logError } from '../utils/errorLogger'; +const PAGE_SIZE = 20; + // ───────────────────────────────────────────────────────────────────────────── // TYPES // ───────────────────────────────────────────────────────────────────────────── @@ -45,31 +48,55 @@ interface Props { const PetPhotosScreen: React.FC = ({ petId, petName, onBack }) => { const [photos, setPhotos] = useState([]); const [loading, setLoading] = useState(true); + const [loadingMore, setLoadingMore] = useState(false); + const [hasMore, setHasMore] = useState(true); + const pageRef = useRef(0); const [uploading, setUploading] = useState(false); const [selectedPhoto, setSelectedPhoto] = useState(null); const [quality, setQuality] = useState('medium'); - // ---- Load photos --------------------------------------------------------- - const loadPhotos = useCallback(async () => { - try { - setLoading(true); - const list = await photoService.listPhotos(petId); - setPhotos(list); - } catch (err) { - logError(err instanceof Error ? err : new Error(String(err)), { - screen: 'PetPhotosScreen', - action: 'loadPhotos', - petId, - }); - Alert.alert('Error', 'Failed to load photos. Please try again.'); - } finally { - setLoading(false); - } - }, [petId]); + // ---- Load photos (paginated) --------------------------------------------- + const loadPhotos = useCallback( + async (reset = false) => { + if (!reset && (!hasMore || loadingMore)) return; + const page = reset ? 0 : pageRef.current; + if (reset) { + setLoading(true); + } else { + setLoadingMore(true); + } + try { + const slice = await photoService.listPhotos(petId, { page, limit: PAGE_SIZE }); + setPhotos((prev) => (reset ? slice : [...prev, ...slice])); + setHasMore(slice.length === PAGE_SIZE); + pageRef.current = page + 1; + } catch (err) { + logError(err instanceof Error ? err : new Error(String(err)), { + screen: 'PetPhotosScreen', + action: 'loadPhotos', + petId, + }); + Alert.alert('Error', 'Failed to load photos. Please try again.'); + } finally { + if (reset) { + setLoading(false); + } else { + setLoadingMore(false); + } + } + }, + [petId, hasMore, loadingMore], + ); useEffect(() => { - void loadPhotos(); - }, [loadPhotos]); + void loadPhotos(true); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [petId]); + + // Prefetch next page when 5 items from end + const handleEndReached = useCallback(() => { + if (hasMore && !loadingMore) void loadPhotos(); + }, [hasMore, loadingMore, loadPhotos]); // ---- Upload a new photo -------------------------------------------------- const handleUpload = useCallback(() => { @@ -86,7 +113,7 @@ const PetPhotosScreen: React.FC = ({ petId, petName, onBack }) => { localUri: asset.uri, quality, }); - await loadPhotos(); + await loadPhotos(true); } catch (err) { logError(err instanceof Error ? err : new Error(String(err)), { screen: 'PetPhotosScreen', @@ -115,7 +142,7 @@ const PetPhotosScreen: React.FC = ({ petId, petName, onBack }) => { try { await photoService.deletePhoto(photo.id); setSelectedPhoto(null); - await loadPhotos(); + await loadPhotos(true); } catch (err) { logError(err instanceof Error ? err : new Error(String(err)), { screen: 'PetPhotosScreen', @@ -193,6 +220,11 @@ const PetPhotosScreen: React.FC = ({ petId, petName, onBack }) => { keyExtractor={(item) => item.id} numColumns={3} contentContainerStyle={styles.grid} + onEndReachedThreshold={5 / PAGE_SIZE} + onEndReached={handleEndReached} + ListFooterComponent={ + loadingMore ? : null + } renderItem={({ item }) => ( = ({ petId, petName, onBack }) => { accessibilityRole="button" accessibilityLabel={item.caption ?? 'Pet photo'} > - + )} /> diff --git a/src/services/photoService.ts b/src/services/photoService.ts index 5ebd3748..94f11736 100644 --- a/src/services/photoService.ts +++ b/src/services/photoService.ts @@ -163,11 +163,23 @@ export async function uploadPhoto(input: UploadPhotoInput): Promise { - const response = await apiClient.get<{ data: PetPhoto[] }>(`/photos/pet/${petId}`); +export async function listPhotos( + petId: string, + options: ListPhotosOptions = {}, +): Promise { + const { page = 0, limit = 20 } = options; + const response = await apiClient.get<{ data: PetPhoto[] }>(`/photos/pet/${petId}`, { + params: { page, limit }, + }); return response.data.data; }