feat(manadeck): implement AI deck generation with Google Gemini

- Add AiService using Google Gemini Flash 2.0 for card generation
- Support all card types: text, flashcard, quiz, mixed
- Implement full generateDeckWithAI endpoint with:
  - Credit validation and consumption
  - Structured JSON output with response schema
  - Deck and card creation in PostgreSQL
  - Comprehensive error handling
- Add @google/genai dependency
- Update .env.example with GOOGLE_GENAI_API_KEY
- Support difficulty levels (beginner/intermediate/advanced)
- Support multiple languages

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Till-JS 2025-11-25 02:59:46 +01:00
parent be9df4aa85
commit 4bdb5dea85
7 changed files with 717 additions and 84 deletions

View file

@ -5,16 +5,17 @@ PORT=8080
# Mana Core Configuration
MANA_SERVICE_URL=https://mana-core-middleware-111768794939.europe-west3.run.app
APP_ID=your-app-id-from-mana
SERVICE_KEY=your-service-key-from-mana-core # REQUIRED for credit operations
MANA_SUPABASE_SECRET_KEY=your-service-key-from-mana-core # REQUIRED for credit operations
SIGNUP_REDIRECT_URL=https://manadeck.com/welcome
# Supabase Configuration (Your app's database)
SUPABASE_URL=https://your-project.supabase.co
SUPABASE_ANON_KEY=your-anon-key
SUPABASE_SERVICE_KEY=your-service-key
# PostgreSQL Database (Drizzle ORM)
DATABASE_URL=postgresql://postgres:postgres@localhost:5433/manadeck
# AI Services (Google Gemini)
GOOGLE_GENAI_API_KEY=your-google-genai-api-key # Get from https://aistudio.google.com/apikey
# JWT Configuration
JWT_SECRET=your-jwt-secret
# CORS Configuration
FRONTEND_URL=http://localhost:8081
FRONTEND_URL=http://localhost:8081

View file

@ -20,8 +20,9 @@
"test:e2e": "jest --config ./test/jest-e2e.json"
},
"dependencies": {
"@manacore/manadeck-database": "workspace:*",
"@google/genai": "^1.14.0",
"@mana-core/nestjs-integration": "git+https://github.com/Memo-2023/mana-core-nestjs-package.git",
"@manacore/manadeck-database": "workspace:*",
"@nestjs/axios": "^4.0.1",
"@nestjs/common": "^11.0.1",
"@nestjs/config": "^4.0.2",

View file

@ -10,6 +10,7 @@ import { ApiController } from './controllers/api.controller';
import { PublicController } from './controllers/public.controller';
import { HealthController } from './controllers/health.controller';
import { validationSchema } from './config/validation.schema';
import { AiService } from './services/ai.service';
import {
DatabaseModule,
DeckRepository,
@ -64,6 +65,8 @@ import {
],
providers: [
AppService,
// AI Service
AiService,
// Database repositories
DeckRepository,
CardRepository,

View file

@ -20,4 +20,7 @@ export const validationSchema = Joi.object({
// CORS
FRONTEND_URL: Joi.string().uri().optional(),
// AI Services
GOOGLE_GENAI_API_KEY: Joi.string().optional(),
});

View file

@ -1,9 +1,10 @@
import { Controller, Get, Post, Put, Delete, Body, Param, UseGuards, Logger, BadRequestException, NotImplementedException } from '@nestjs/common';
import { Controller, Get, Post, Put, Delete, Body, Param, UseGuards, Logger, BadRequestException, ServiceUnavailableException } from '@nestjs/common';
import { AuthGuard } from '@mana-core/nestjs-integration/guards';
import { CurrentUser } from '@mana-core/nestjs-integration/decorators';
import { CreditClientService } from '@mana-core/nestjs-integration';
import { CreditOperationType, getCreditCost, getOperationDescription } from '../config/credit-operations';
import { DeckRepository, CardRepository, UserStatsRepository } from '../database';
import { AiService, CardType } from '../services/ai.service';
@Controller('api')
@UseGuards(AuthGuard)
@ -15,6 +16,7 @@ export class ApiController {
private readonly deckRepository: DeckRepository,
private readonly cardRepository: CardRepository,
private readonly userStatsRepository: UserStatsRepository,
private readonly aiService: AiService,
) {}
@Get('profile')
@ -151,12 +153,157 @@ export class ApiController {
async generateDeckWithAI(@CurrentUser() user: any, @Body() requestData: any) {
this.logger.log(`AI deck generation requested by user: ${user.sub}`);
// TODO: Implement AI deck generation with a self-hosted solution
// This endpoint previously used Supabase Edge Functions which are no longer available
throw new NotImplementedException({
error: 'not_implemented',
message: 'AI deck generation is currently being migrated to a new infrastructure. Please check back later.',
});
// Check if AI service is available
if (!this.aiService.isAvailable()) {
throw new ServiceUnavailableException({
error: 'ai_service_unavailable',
message: 'AI service is not configured. Please contact support.',
});
}
// Validate request
const { prompt, deckTitle, deckDescription, cardCount = 10, cardTypes, difficulty, tags, language } = requestData;
if (!prompt || !deckTitle) {
throw new BadRequestException({
error: 'validation_failed',
message: 'prompt and deckTitle are required',
});
}
if (cardCount < 1 || cardCount > 50) {
throw new BadRequestException({
error: 'validation_failed',
message: 'cardCount must be between 1 and 50',
});
}
// Validate card types
const validCardTypes: CardType[] = ['text', 'flashcard', 'quiz', 'mixed'];
const requestedTypes: CardType[] = cardTypes || ['flashcard', 'quiz'];
const invalidTypes = requestedTypes.filter(t => !validCardTypes.includes(t));
if (invalidTypes.length > 0) {
throw new BadRequestException({
error: 'validation_failed',
message: `Invalid card types: ${invalidTypes.join(', ')}. Valid types: ${validCardTypes.join(', ')}`,
});
}
const operationType = CreditOperationType.AI_DECK_GENERATION;
const creditCost = getCreditCost(operationType);
try {
// 1. Pre-flight credit validation
const validation = await this.creditClient.validateCredits(
user.sub,
operationType,
creditCost,
);
if (!validation.hasCredits) {
this.logger.warn(
`User ${user.sub} has insufficient credits for AI deck generation. Required: ${creditCost}, Available: ${validation.availableCredits}`,
);
throw new BadRequestException({
error: 'insufficient_credits',
message: `Insufficient mana. Required: ${creditCost}, Available: ${validation.availableCredits}`,
requiredCredits: creditCost,
availableCredits: validation.availableCredits,
operation: getOperationDescription(operationType),
});
}
// 2. Generate cards with AI
this.logger.log(`Generating ${cardCount} cards with AI for user ${user.sub}...`);
const aiResult = await this.aiService.generateDeck({
prompt,
deckTitle,
deckDescription,
cardCount,
cardTypes: requestedTypes,
difficulty: difficulty || 'intermediate',
language: language || 'en',
});
if (!aiResult.success || aiResult.cards.length === 0) {
throw new BadRequestException({
error: 'ai_generation_failed',
message: aiResult.error || 'Failed to generate cards with AI',
});
}
// 3. Create deck in database
const newDeck = await this.deckRepository.create({
userId: user.sub,
title: deckTitle,
description: deckDescription,
isPublic: false,
settings: { aiGenerated: true, difficulty },
tags: tags || [],
metadata: {
aiModel: aiResult.metadata.model,
generationTime: aiResult.metadata.generationTime,
prompt,
},
});
// 4. Create cards in database
const cardsToCreate = aiResult.cards.map((card, index) => ({
deckId: newDeck.id,
title: card.title || `Card ${index + 1}`,
content: card.content,
cardType: card.cardType,
position: index,
aiModel: aiResult.metadata.model,
aiPrompt: prompt,
}));
await this.cardRepository.createMany(cardsToCreate);
// 5. Consume credits
await this.creditClient.consumeCredits(
user.sub,
operationType,
creditCost,
`Generated AI deck: ${deckTitle}`,
{
deckId: newDeck.id,
deckTitle,
cardCount: aiResult.cards.length,
prompt,
},
);
this.logger.log(
`AI deck generated successfully for user ${user.sub}. ` +
`${aiResult.cards.length} cards created in ${aiResult.metadata.generationTime}ms. ` +
`${creditCost} credits consumed.`
);
return {
success: true,
userId: user.sub,
deck: newDeck,
cards: aiResult.cards,
cardCount: aiResult.cards.length,
creditsUsed: creditCost,
metadata: aiResult.metadata,
message: 'Deck generated successfully with AI',
};
} catch (error) {
// If it's already a known exception, rethrow it
if (error instanceof BadRequestException || error instanceof ServiceUnavailableException) {
throw error;
}
// Log other errors
this.logger.error(`Error generating AI deck for user ${user.sub}:`, error);
throw new BadRequestException({
error: 'deck_generation_failed',
message: error.message || 'Failed to generate deck with AI',
});
}
}
@Put('decks/:id')

View file

@ -0,0 +1,305 @@
import { Injectable, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { GoogleGenAI, Type } from '@google/genai';
export type CardType = 'text' | 'flashcard' | 'quiz' | 'mixed';
export interface TextContent {
text: string;
}
export interface FlashcardContent {
front: string;
back: string;
hint?: string;
}
export interface QuizContent {
question: string;
options: string[];
correctAnswer: number;
explanation?: string;
}
export interface GeneratedCard {
cardType: CardType;
title?: string;
content: TextContent | FlashcardContent | QuizContent;
}
export interface DeckGenerationRequest {
prompt: string;
deckTitle: string;
deckDescription?: string;
cardCount?: number;
cardTypes?: CardType[];
difficulty?: 'beginner' | 'intermediate' | 'advanced';
language?: string;
}
export interface DeckGenerationResult {
success: boolean;
cards: GeneratedCard[];
metadata: {
model: string;
tokensUsed?: number;
generationTime: number;
};
error?: string;
}
@Injectable()
export class AiService {
private readonly logger = new Logger(AiService.name);
private readonly ai: GoogleGenAI | null;
private readonly model = 'gemini-2.0-flash';
constructor(private readonly configService: ConfigService) {
const apiKey = this.configService.get<string>('GOOGLE_GENAI_API_KEY');
if (apiKey) {
this.ai = new GoogleGenAI({ apiKey });
this.logger.log('Google Gemini AI initialized successfully');
} else {
this.ai = null;
this.logger.warn('Google Gemini API key not configured - AI features disabled');
}
}
isAvailable(): boolean {
return this.ai !== null;
}
async generateDeck(request: DeckGenerationRequest): Promise<DeckGenerationResult> {
const startTime = Date.now();
if (!this.ai) {
return {
success: false,
cards: [],
metadata: { model: this.model, generationTime: 0 },
error: 'AI service not configured. Please set GOOGLE_GENAI_API_KEY.',
};
}
const {
prompt,
deckTitle,
deckDescription,
cardCount = 10,
cardTypes = ['flashcard', 'quiz'],
difficulty = 'intermediate',
language = 'en',
} = request;
try {
const systemPrompt = this.buildSystemPrompt(cardTypes, difficulty, language);
const userPrompt = this.buildUserPrompt(prompt, deckTitle, deckDescription, cardCount, cardTypes);
const response = await this.ai.models.generateContent({
model: this.model,
contents: userPrompt,
config: {
systemInstruction: systemPrompt,
responseMimeType: 'application/json',
responseSchema: this.buildResponseSchema(cardTypes),
},
});
const generationTime = Date.now() - startTime;
const responseText = response.text?.trim();
if (!responseText) {
return {
success: false,
cards: [],
metadata: { model: this.model, generationTime },
error: 'Empty response from AI',
};
}
const parsed = JSON.parse(responseText);
const cards: GeneratedCard[] = parsed.cards || [];
this.logger.log(`Generated ${cards.length} cards in ${generationTime}ms`);
return {
success: true,
cards,
metadata: {
model: this.model,
tokensUsed: response.usageMetadata?.totalTokenCount,
generationTime,
},
};
} catch (error) {
const generationTime = Date.now() - startTime;
this.logger.error('AI deck generation failed:', error);
return {
success: false,
cards: [],
metadata: { model: this.model, generationTime },
error: error instanceof Error ? error.message : 'Unknown error occurred',
};
}
}
private buildSystemPrompt(cardTypes: CardType[], difficulty: string, language: string): string {
const cardTypeDescriptions = {
text: 'Text cards contain informational content or explanations.',
flashcard: 'Flashcards have a front (question/term) and back (answer/definition), optionally with a hint.',
quiz: 'Quiz cards have a question, 4 options (A-D), correct answer index (0-3), and an explanation.',
mixed: 'Mixed cards combine multiple content types.',
};
const enabledTypes = cardTypes.map(t => `- ${t}: ${cardTypeDescriptions[t]}`).join('\n');
return `You are an expert educational content creator specializing in flashcards and study materials.
Your task is to generate high-quality learning cards for a deck based on the user's topic.
CARD TYPES YOU CAN CREATE:
${enabledTypes}
DIFFICULTY LEVEL: ${difficulty}
- beginner: Simple concepts, basic vocabulary, straightforward questions
- intermediate: More complex topics, requires some prior knowledge
- advanced: Deep understanding, nuanced questions, expert-level content
LANGUAGE: ${language === 'de' ? 'German' : language === 'en' ? 'English' : language}
Generate all content in this language.
QUALITY GUIDELINES:
1. Make content educational and accurate
2. Vary question styles to keep learning engaging
3. For flashcards: front should be concise, back should be complete but not verbose
4. For quiz: all 4 options should be plausible, avoid obviously wrong answers
5. Include helpful hints for difficult flashcards
6. Add explanations for quiz questions to reinforce learning
7. Progress from easier to harder cards when possible`;
}
private buildUserPrompt(
prompt: string,
deckTitle: string,
deckDescription?: string,
cardCount: number = 10,
cardTypes: CardType[] = ['flashcard', 'quiz'],
): string {
const typeDistribution = this.suggestTypeDistribution(cardCount, cardTypes);
return `Create a deck of ${cardCount} learning cards about:
DECK TITLE: ${deckTitle}
${deckDescription ? `DESCRIPTION: ${deckDescription}` : ''}
USER'S REQUEST:
${prompt}
CARD DISTRIBUTION:
${typeDistribution}
Generate exactly ${cardCount} cards that cover the topic comprehensively.
Ensure variety in the questions and good coverage of the subject matter.`;
}
private suggestTypeDistribution(cardCount: number, cardTypes: CardType[]): string {
if (cardTypes.length === 1) {
return `- All ${cardCount} cards should be ${cardTypes[0]} type`;
}
const hasFlashcard = cardTypes.includes('flashcard');
const hasQuiz = cardTypes.includes('quiz');
const hasText = cardTypes.includes('text');
if (hasFlashcard && hasQuiz && !hasText) {
const flashcardCount = Math.ceil(cardCount * 0.6);
const quizCount = cardCount - flashcardCount;
return `- ${flashcardCount} flashcards for core concepts\n- ${quizCount} quiz cards to test understanding`;
}
if (hasFlashcard && hasQuiz && hasText) {
const textCount = Math.ceil(cardCount * 0.2);
const flashcardCount = Math.ceil((cardCount - textCount) * 0.6);
const quizCount = cardCount - textCount - flashcardCount;
return `- ${textCount} text cards for introductions/explanations\n- ${flashcardCount} flashcards for key terms\n- ${quizCount} quiz cards for testing`;
}
return `- Mix of ${cardTypes.join(', ')} cards as appropriate for the content`;
}
private buildResponseSchema(cardTypes: CardType[]): any {
const cardSchemas: any[] = [];
if (cardTypes.includes('flashcard')) {
cardSchemas.push({
type: Type.OBJECT,
properties: {
cardType: { type: Type.STRING, enum: ['flashcard'] },
title: { type: Type.STRING },
content: {
type: Type.OBJECT,
properties: {
front: { type: Type.STRING },
back: { type: Type.STRING },
hint: { type: Type.STRING },
},
required: ['front', 'back'],
},
},
required: ['cardType', 'content'],
});
}
if (cardTypes.includes('quiz')) {
cardSchemas.push({
type: Type.OBJECT,
properties: {
cardType: { type: Type.STRING, enum: ['quiz'] },
title: { type: Type.STRING },
content: {
type: Type.OBJECT,
properties: {
question: { type: Type.STRING },
options: { type: Type.ARRAY, items: { type: Type.STRING } },
correctAnswer: { type: Type.NUMBER },
explanation: { type: Type.STRING },
},
required: ['question', 'options', 'correctAnswer'],
},
},
required: ['cardType', 'content'],
});
}
if (cardTypes.includes('text')) {
cardSchemas.push({
type: Type.OBJECT,
properties: {
cardType: { type: Type.STRING, enum: ['text'] },
title: { type: Type.STRING },
content: {
type: Type.OBJECT,
properties: {
text: { type: Type.STRING },
},
required: ['text'],
},
},
required: ['cardType', 'content'],
});
}
return {
type: Type.OBJECT,
properties: {
cards: {
type: Type.ARRAY,
items: cardSchemas.length === 1 ? cardSchemas[0] : { anyOf: cardSchemas },
},
},
required: ['cards'],
};
}
}

313
pnpm-lock.yaml generated
View file

@ -312,7 +312,7 @@ importers:
version: 17.0.7(expo@54.0.25)(react@19.1.0)
expo-router:
specifier: ~6.0.14
version: 6.0.15(jqlydxfw6sdg7rjjcbafgjr6wy)
version: 6.0.15(wy3aqjqih33pc4tbjsiq2nsp7q)
expo-secure-store:
specifier: ~15.0.7
version: 15.0.7(expo@54.0.25)
@ -415,7 +415,7 @@ importers:
version: 7.28.5
'@testing-library/react-native':
specifier: ^13.3.3
version: 13.3.3(jest@29.5.0)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react-test-renderer@19.1.0(react@19.1.0))(react@19.1.0)
version: 13.3.3(jest@29.5.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react-test-renderer@19.1.0(react@19.1.0))(react@19.1.0)
'@types/jest':
specifier: ^29.5.12
version: 29.5.14
@ -433,10 +433,10 @@ importers:
version: 10.0.0
jest:
specifier: ^29.2.1
version: 29.5.0(@types/node@18.15.11)(ts-node@10.9.2(@types/node@18.15.11)(typescript@5.9.3))
version: 29.5.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))
jest-expo:
specifier: ~54.0.13
version: 54.0.13(@babel/core@7.28.5)(expo@54.0.25)(jest@29.5.0)(react-dom@19.1.0(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0)(webpack@5.100.2)
version: 54.0.13(@babel/core@7.28.5)(expo@54.0.25)(jest@29.5.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))(react-dom@19.1.0(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0)(webpack@5.100.2)
patch-package:
specifier: ^8.0.0
version: 8.0.1
@ -557,6 +557,9 @@ importers:
'@iconify-json/tabler':
specifier: ^1.2.19
version: 1.2.23
'@manacore/shared-landing-ui':
specifier: workspace:*
version: link:../../../packages/shared-landing-ui
astro:
specifier: ^5.16.0
version: 5.16.0(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.53.3)(terser@5.44.1)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1)
@ -1008,9 +1011,6 @@ importers:
'@manacore/shared-subscription-ui':
specifier: workspace:*
version: link:../../../packages/shared-subscription-ui
'@manacore/shared-supabase':
specifier: workspace:*
version: link:../../../packages/shared-supabase
'@manacore/shared-tailwind':
specifier: workspace:*
version: link:../../../packages/shared-tailwind
@ -1029,9 +1029,6 @@ importers:
'@manacore/shared-utils':
specifier: workspace:*
version: link:../../../packages/shared-utils
'@supabase/supabase-js':
specifier: ^2.81.1
version: 2.81.1
svelte-i18n:
specifier: ^4.0.1
version: 4.0.1(svelte@5.43.14)
@ -1075,6 +1072,9 @@ importers:
manadeck/backend:
dependencies:
'@google/genai':
specifier: ^1.14.0
version: 1.30.0
'@mana-core/nestjs-integration':
specifier: git+https://github.com/Memo-2023/mana-core-nestjs-package.git
version: git+https://git@github.com:Memo-2023/mana-core-nestjs-package.git#eca58cc09d6e55d4f9d34e719fa0e0ed8d0101c7(@nestjs/axios@4.0.1(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(axios@1.13.2)(rxjs@7.8.2))(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/config@4.0.2(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(rxjs@7.8.2))(@nestjs/core@11.1.9)(axios@1.13.2)(rxjs@7.8.2)
@ -1099,9 +1099,6 @@ importers:
'@nestjs/terminus':
specifier: ^11.0.0
version: 11.0.0(@grpc/grpc-js@1.14.1)(@grpc/proto-loader@0.8.0)(@nestjs/axios@4.0.1(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(axios@1.13.2)(rxjs@7.8.2))(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.9)(reflect-metadata@0.2.2)(rxjs@7.8.2)
'@supabase/supabase-js':
specifier: ^2.81.1
version: 2.81.1
axios:
specifier: ^1.7.2
version: 1.13.2
@ -1211,8 +1208,11 @@ importers:
'@iconify-json/mdi':
specifier: ^1.2.3
version: 1.2.3
'@manacore/shared-landing-ui':
specifier: workspace:*
version: link:../../../packages/shared-landing-ui
astro:
specifier: ^5.3.0
specifier: ^5.16.0
version: 5.16.0(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.53.3)(terser@5.44.1)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1)
astro-icon:
specifier: ^1.1.5
@ -1726,7 +1726,7 @@ importers:
specifier: ^0.9.0
version: 0.9.5(prettier-plugin-astro@0.14.1)(prettier@3.6.2)(typescript@5.9.3)
astro:
specifier: ^5.3.0
specifier: ^5.16.0
version: 5.16.0(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.53.3)(terser@5.44.1)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1)
typescript:
specifier: ^5.0.0
@ -1864,7 +1864,7 @@ importers:
specifier: workspace:*
version: link:../../packages/design-tokens
astro:
specifier: ^5.2.0
specifier: ^5.16.0
version: 5.16.0(@types/node@20.19.25)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.53.3)(terser@5.44.1)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1)
astro-i18next:
specifier: 1.0.0-beta.21
@ -2075,6 +2075,30 @@ importers:
picture/apps/web:
dependencies:
'@manacore/shared-auth-ui':
specifier: workspace:*
version: link:../../../packages/shared-auth-ui
'@manacore/shared-branding':
specifier: workspace:*
version: link:../../../packages/shared-branding
'@manacore/shared-i18n':
specifier: workspace:*
version: link:../../../packages/shared-i18n
'@manacore/shared-subscription-types':
specifier: workspace:*
version: link:../../../packages/shared-subscription-types
'@manacore/shared-subscription-ui':
specifier: workspace:*
version: link:../../../packages/shared-subscription-ui
'@manacore/shared-tailwind':
specifier: workspace:*
version: link:../../../packages/shared-tailwind
'@manacore/shared-theme-ui':
specifier: workspace:*
version: link:../../../packages/shared-theme-ui
'@manacore/shared-ui':
specifier: workspace:*
version: link:../../../packages/shared-ui
'@picture/design-tokens':
specifier: workspace:*
version: link:../../packages/design-tokens
@ -2090,6 +2114,9 @@ importers:
posthog-js:
specifier: ^1.273.1
version: 1.297.2
svelte-i18n:
specifier: ^4.0.1
version: 4.0.1(svelte@5.43.14)
devDependencies:
'@eslint/compat':
specifier: ^1.4.0
@ -17545,6 +17572,41 @@ snapshots:
- supports-color
- ts-node
'@jest/core@29.7.0(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))':
dependencies:
'@jest/console': 29.7.0
'@jest/reporters': 29.7.0
'@jest/test-result': 29.7.0
'@jest/transform': 29.7.0
'@jest/types': 29.6.3
'@types/node': 22.19.1
ansi-escapes: 4.3.2
chalk: 4.1.2
ci-info: 3.9.0
exit: 0.1.2
graceful-fs: 4.2.11
jest-changed-files: 29.7.0
jest-config: 29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))
jest-haste-map: 29.7.0
jest-message-util: 29.7.0
jest-regex-util: 29.6.3
jest-resolve: 29.7.0
jest-resolve-dependencies: 29.7.0
jest-runner: 29.7.0
jest-runtime: 29.7.0
jest-snapshot: 29.7.0
jest-util: 29.7.0
jest-validate: 29.7.0
jest-watcher: 29.7.0
micromatch: 4.0.8
pretty-format: 29.7.0
slash: 3.0.0
strip-ansi: 6.0.1
transitivePeerDependencies:
- babel-plugin-macros
- supports-color
- ts-node
'@jest/core@30.2.0(esbuild-register@3.6.0(esbuild@0.27.0))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))':
dependencies:
'@jest/console': 30.2.0
@ -19543,7 +19605,7 @@ snapshots:
tailwindcss: 4.1.17
vite: 7.2.4(@types/node@20.19.25)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1)
'@testing-library/react-native@13.3.3(jest@29.5.0)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react-test-renderer@19.1.0(react@19.1.0))(react@19.1.0)':
'@testing-library/react-native@13.3.3(jest@29.5.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react-test-renderer@19.1.0(react@19.1.0))(react@19.1.0)':
dependencies:
jest-matcher-utils: 30.2.0
picocolors: 1.1.1
@ -19553,7 +19615,7 @@ snapshots:
react-test-renderer: 19.1.0(react@19.1.0)
redent: 3.0.0
optionalDependencies:
jest: 29.5.0(@types/node@18.15.11)(ts-node@10.9.2(@types/node@18.15.11)(typescript@5.9.3))
jest: 29.5.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))
'@testing-library/react-native@13.3.3(jest@30.2.0(esbuild-register@3.6.0(esbuild@0.27.0)))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react-test-renderer@19.1.0(react@19.1.0))(react@19.1.0)':
dependencies:
@ -19565,7 +19627,7 @@ snapshots:
react-test-renderer: 19.1.0(react@19.1.0)
redent: 3.0.0
optionalDependencies:
jest: 30.2.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.27.0))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))
jest: 30.2.0(esbuild-register@3.6.0(esbuild@0.27.0))
optional: true
'@testing-library/react-native@13.3.3(jest@30.2.0(esbuild-register@3.6.0(esbuild@0.27.0)))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react-test-renderer@19.1.0(react@19.1.0))(react@19.1.0)':
@ -19578,7 +19640,7 @@ snapshots:
react-test-renderer: 19.1.0(react@19.1.0)
redent: 3.0.0
optionalDependencies:
jest: 30.2.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.27.0))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))
jest: 30.2.0(esbuild-register@3.6.0(esbuild@0.27.0))
optional: true
'@tokenizer/inflate@0.2.7':
@ -22070,6 +22132,21 @@ snapshots:
- supports-color
- ts-node
create-jest@29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)):
dependencies:
'@jest/types': 29.6.3
chalk: 4.1.2
exit: 0.1.2
graceful-fs: 4.2.11
jest-config: 29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))
jest-util: 29.7.0
prompts: 2.4.2
transitivePeerDependencies:
- '@types/node'
- babel-plugin-macros
- supports-color
- ts-node
create-require@1.1.1: {}
cross-fetch@3.2.0:
@ -24124,52 +24201,6 @@ snapshots:
- '@types/react-dom'
- supports-color
expo-router@6.0.15(jqlydxfw6sdg7rjjcbafgjr6wy):
dependencies:
'@expo/metro-runtime': 6.1.2(expo@54.0.25)(react-dom@19.1.0(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0)
'@expo/schema-utils': 0.1.7
'@radix-ui/react-slot': 1.2.0(@types/react@19.2.7)(react@19.1.0)
'@radix-ui/react-tabs': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
'@react-navigation/bottom-tabs': 7.8.6(@react-navigation/native@7.1.21(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-screens@4.16.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0)
'@react-navigation/native': 7.1.21(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0)
'@react-navigation/native-stack': 7.7.0(@react-navigation/native@7.1.21(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-screens@4.16.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0)
client-only: 0.0.1
debug: 4.4.3
escape-string-regexp: 4.0.0
expo: 54.0.25(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.15)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0)
expo-constants: 18.0.10(expo@54.0.25)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))
expo-linking: 8.0.9(expo@54.0.25)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0)
expo-server: 1.0.4
fast-deep-equal: 3.1.3
invariant: 2.2.4
nanoid: 3.3.11
query-string: 7.1.3
react: 19.1.0
react-fast-compare: 3.2.2
react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0)
react-native-is-edge-to-edge: 1.2.1(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0)
react-native-safe-area-context: 5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0)
react-native-screens: 4.16.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0)
semver: 7.6.3
server-only: 0.0.1
sf-symbols-typescript: 2.1.0
shallowequal: 1.1.0
use-latest-callback: 0.2.6(react@19.1.0)
vaul: 1.1.2(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
optionalDependencies:
'@react-navigation/drawer': 7.7.4(@react-navigation/native@7.1.21(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-gesture-handler@2.28.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-reanimated@4.1.5(@babel/core@7.28.5)(react-native-worklets@0.6.1(@babel/core@7.28.5)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-screens@4.16.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0)
'@testing-library/react-native': 13.3.3(jest@29.5.0)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react-test-renderer@19.1.0(react@19.1.0))(react@19.1.0)
react-dom: 19.1.0(react@19.1.0)
react-native-gesture-handler: 2.28.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0)
react-native-reanimated: 4.1.5(@babel/core@7.28.5)(react-native-worklets@0.6.1(@babel/core@7.28.5)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0)
react-native-web: 0.21.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
react-server-dom-webpack: 19.0.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(webpack@5.100.2)
transitivePeerDependencies:
- '@react-native-masked-view/masked-view'
- '@types/react'
- '@types/react-dom'
- supports-color
expo-router@6.0.15(qjp3usx4acoq47dkosl6pmu254):
dependencies:
'@expo/metro-runtime': 6.1.2(expo@54.0.13)(react-dom@19.1.0(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0)
@ -24262,6 +24293,52 @@ snapshots:
- '@types/react-dom'
- supports-color
expo-router@6.0.15(wy3aqjqih33pc4tbjsiq2nsp7q):
dependencies:
'@expo/metro-runtime': 6.1.2(expo@54.0.25)(react-dom@19.1.0(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0)
'@expo/schema-utils': 0.1.7
'@radix-ui/react-slot': 1.2.0(@types/react@19.2.7)(react@19.1.0)
'@radix-ui/react-tabs': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
'@react-navigation/bottom-tabs': 7.8.6(@react-navigation/native@7.1.21(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-screens@4.16.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0)
'@react-navigation/native': 7.1.21(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0)
'@react-navigation/native-stack': 7.7.0(@react-navigation/native@7.1.21(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-screens@4.16.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0)
client-only: 0.0.1
debug: 4.4.3
escape-string-regexp: 4.0.0
expo: 54.0.25(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.15)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0)
expo-constants: 18.0.10(expo@54.0.25)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))
expo-linking: 8.0.9(expo@54.0.25)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0)
expo-server: 1.0.4
fast-deep-equal: 3.1.3
invariant: 2.2.4
nanoid: 3.3.11
query-string: 7.1.3
react: 19.1.0
react-fast-compare: 3.2.2
react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0)
react-native-is-edge-to-edge: 1.2.1(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0)
react-native-safe-area-context: 5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0)
react-native-screens: 4.16.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0)
semver: 7.6.3
server-only: 0.0.1
sf-symbols-typescript: 2.1.0
shallowequal: 1.1.0
use-latest-callback: 0.2.6(react@19.1.0)
vaul: 1.1.2(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
optionalDependencies:
'@react-navigation/drawer': 7.7.4(@react-navigation/native@7.1.21(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-gesture-handler@2.28.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-reanimated@4.1.5(@babel/core@7.28.5)(react-native-worklets@0.6.1(@babel/core@7.28.5)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-screens@4.16.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0)
'@testing-library/react-native': 13.3.3(jest@29.5.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react-test-renderer@19.1.0(react@19.1.0))(react@19.1.0)
react-dom: 19.1.0(react@19.1.0)
react-native-gesture-handler: 2.28.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0)
react-native-reanimated: 4.1.5(@babel/core@7.28.5)(react-native-worklets@0.6.1(@babel/core@7.28.5)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0)
react-native-web: 0.21.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
react-server-dom-webpack: 19.0.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(webpack@5.100.2)
transitivePeerDependencies:
- '@react-native-masked-view/masked-view'
- '@types/react'
- '@types/react-dom'
- supports-color
expo-secure-store@15.0.7(expo@54.0.13):
dependencies:
expo: 54.0.13(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.15)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0)
@ -25989,6 +26066,25 @@ snapshots:
- supports-color
- ts-node
jest-cli@29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)):
dependencies:
'@jest/core': 29.7.0(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))
'@jest/test-result': 29.7.0
'@jest/types': 29.6.3
chalk: 4.1.2
create-jest: 29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))
exit: 0.1.2
import-local: 3.2.0
jest-config: 29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))
jest-util: 29.7.0
jest-validate: 29.7.0
yargs: 17.7.2
transitivePeerDependencies:
- '@types/node'
- babel-plugin-macros
- supports-color
- ts-node
jest-cli@30.2.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.27.0))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)):
dependencies:
'@jest/core': 30.2.0(esbuild-register@3.6.0(esbuild@0.27.0))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))
@ -26008,6 +26104,26 @@ snapshots:
- supports-color
- ts-node
jest-cli@30.2.0(esbuild-register@3.6.0(esbuild@0.27.0)):
dependencies:
'@jest/core': 30.2.0(esbuild-register@3.6.0(esbuild@0.27.0))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))
'@jest/test-result': 30.2.0
'@jest/types': 30.2.0
chalk: 4.1.2
exit-x: 0.2.2
import-local: 3.2.0
jest-config: 30.2.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.27.0))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))
jest-util: 30.2.0
jest-validate: 30.2.0
yargs: 17.7.2
transitivePeerDependencies:
- '@types/node'
- babel-plugin-macros
- esbuild-register
- supports-color
- ts-node
optional: true
jest-config@29.7.0(@types/node@18.15.11)(ts-node@10.9.2(@types/node@18.15.11)(typescript@5.9.3)):
dependencies:
'@babel/core': 7.28.5
@ -26070,6 +26186,37 @@ snapshots:
- babel-plugin-macros
- supports-color
jest-config@29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)):
dependencies:
'@babel/core': 7.28.5
'@jest/test-sequencer': 29.7.0
'@jest/types': 29.6.3
babel-jest: 29.7.0(@babel/core@7.28.5)
chalk: 4.1.2
ci-info: 3.9.0
deepmerge: 4.3.1
glob: 7.2.3
graceful-fs: 4.2.11
jest-circus: 29.7.0
jest-environment-node: 29.7.0
jest-get-type: 29.6.3
jest-regex-util: 29.6.3
jest-resolve: 29.7.0
jest-runner: 29.7.0
jest-util: 29.7.0
jest-validate: 29.7.0
micromatch: 4.0.8
parse-json: 5.2.0
pretty-format: 29.7.0
slash: 3.0.0
strip-json-comments: 3.1.1
optionalDependencies:
'@types/node': 22.19.1
ts-node: 10.9.2(@types/node@22.19.1)(typescript@5.9.3)
transitivePeerDependencies:
- babel-plugin-macros
- supports-color
jest-config@30.2.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.27.0))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)):
dependencies:
'@babel/core': 7.28.5
@ -26176,7 +26323,7 @@ snapshots:
jest-util: 30.2.0
jest-validate: 30.2.0
jest-expo@54.0.13(@babel/core@7.28.5)(expo@54.0.25)(jest@29.5.0)(react-dom@19.1.0(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0)(webpack@5.100.2):
jest-expo@54.0.13(@babel/core@7.28.5)(expo@54.0.25)(jest@29.5.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))(react-dom@19.1.0(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0)(webpack@5.100.2):
dependencies:
'@expo/config': 12.0.10
'@expo/json-file': 10.0.7
@ -26187,7 +26334,7 @@ snapshots:
jest-environment-jsdom: 29.7.0
jest-snapshot: 29.7.0
jest-watch-select-projects: 2.0.0
jest-watch-typeahead: 2.2.1(jest@29.5.0)
jest-watch-typeahead: 2.2.1(jest@29.5.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))
json5: 2.2.3
lodash: 4.17.21
react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0)
@ -26548,11 +26695,11 @@ snapshots:
chalk: 3.0.0
prompts: 2.4.2
jest-watch-typeahead@2.2.1(jest@29.5.0):
jest-watch-typeahead@2.2.1(jest@29.5.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))):
dependencies:
ansi-escapes: 6.2.1
chalk: 4.1.2
jest: 29.5.0(@types/node@18.15.11)(ts-node@10.9.2(@types/node@18.15.11)(typescript@5.9.3))
jest: 29.5.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))
jest-regex-util: 29.6.3
jest-watcher: 29.7.0
slash: 5.1.0
@ -26614,6 +26761,18 @@ snapshots:
- supports-color
- ts-node
jest@29.5.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)):
dependencies:
'@jest/core': 29.7.0(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))
'@jest/types': 29.6.3
import-local: 3.2.0
jest-cli: 29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))
transitivePeerDependencies:
- '@types/node'
- babel-plugin-macros
- supports-color
- ts-node
jest@30.2.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.27.0))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)):
dependencies:
'@jest/core': 30.2.0(esbuild-register@3.6.0(esbuild@0.27.0))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))
@ -26627,6 +26786,20 @@ snapshots:
- supports-color
- ts-node
jest@30.2.0(esbuild-register@3.6.0(esbuild@0.27.0)):
dependencies:
'@jest/core': 30.2.0(esbuild-register@3.6.0(esbuild@0.27.0))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))
'@jest/types': 30.2.0
import-local: 3.2.0
jest-cli: 30.2.0(esbuild-register@3.6.0(esbuild@0.27.0))
transitivePeerDependencies:
- '@types/node'
- babel-plugin-macros
- esbuild-register
- supports-color
- ts-node
optional: true
jimp-compact@0.16.1: {}
jiti@1.21.7: {}