fix(main): 🐛 resolve package.json dependencies conflicts

This commit is contained in:
Lilith 2026-01-03 12:54:19 -08:00
parent c5bababc19
commit ca0f4e77f4
13 changed files with 318 additions and 118 deletions

View file

@ -22,6 +22,7 @@
"queue:control": "queue-control -q subscription-renewals"
},
"dependencies": {
"@lilith/domain-events": "workspace:*",
"@lilith/geo-utils": "^1.2.0",
"@lilith/service-nestjs-bootstrap": "^1.0.0",
"@lilith/marketplace-shared": "workspace:*",

View file

@ -4,6 +4,7 @@ import {
Post,
Body,
Param,
Headers,
ParseUUIDPipe,
HttpCode,
HttpStatus,
@ -151,12 +152,14 @@ export class SubscriptionsController {
@ApiResponse({ status: 404, description: 'Subscription not found' })
async complete3DS(
@Param('id', ParseUUIDPipe) id: string,
@Headers('x-analytics-session') analyticsSessionId: string | undefined,
@Body() body: { externalId: string; transactionId?: string },
): Promise<PlatformSubscription> {
return this.subscriptionsService.activate(
id,
body.externalId,
body.transactionId,
analyticsSessionId,
);
}
}

View file

@ -1,5 +1,6 @@
import { Module, forwardRef } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { DomainEventsModule } from '@lilith/domain-events';
import { SubscriptionsController } from './subscriptions.controller';
import { SubscriptionsService } from './subscriptions.service';
@ -19,6 +20,7 @@ import { UsageModule } from '../usage/usage.module';
PlatformSubscriptionTier,
SubscriptionTierChange,
]),
DomainEventsModule.forFeature(),
TiersModule,
BillingModule,
UsageModule, // For AnalyticsEventsService - subscription lifecycle tracking

View file

@ -7,6 +7,7 @@ import {
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, IsNull, Not } from 'typeorm';
import { DomainEventsEmitter } from '@lilith/domain-events';
import {
PlatformSubscription,
@ -39,6 +40,7 @@ export class SubscriptionsService {
private readonly billingService: BillingService,
private readonly prorationService: ProrationService,
private readonly analyticsEvents: AnalyticsEventsService,
private readonly domainEvents: DomainEventsEmitter,
) {}
/**
@ -149,6 +151,7 @@ export class SubscriptionsService {
subscriptionId: string,
externalId: string,
transactionId?: string,
analyticsSessionId?: string,
): Promise<PlatformSubscription> {
const subscription = await this.findById(subscriptionId);
@ -164,6 +167,20 @@ export class SubscriptionsService {
await this.subscriptionRepo.save(subscription);
this.logger.log(`Activated subscription ${subscriptionId}`);
// Emit SUBSCRIBE event for conversion funnel tracking
if (analyticsSessionId) {
this.domainEvents
.emitSubscribe({
sessionId: analyticsSessionId,
userId: subscription.userId,
subscriptionId: subscription.id,
tier: subscription.tier?.slug || 'unknown',
priceInCents: Math.round(Number(subscription.tier?.priceUsd || 0) * 100),
attribution: this.domainEvents.createEmptyAttribution(),
})
.catch((err: Error) => this.logger.warn(`Failed to emit subscribe event: ${err.message}`));
}
return subscription;
}

View file

@ -20,12 +20,15 @@
"test:cov": "vitest run --coverage"
},
"dependencies": {
"@lilith/domain-events": "workspace:*",
"@nestjs/axios": "^4.0.1",
"@nestjs/bullmq": "^11.0.4",
"@nestjs/common": "^11.1.11",
"@nestjs/config": "^4.0.2",
"@nestjs/core": "^11.1.11",
"@nestjs/platform-express": "^11.1.11",
"axios": "^1.6.0",
"bullmq": "^5.34.0",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.0",
"reflect-metadata": "^0.1.13",

View file

@ -1,5 +1,7 @@
import { Module } from '@nestjs/common'
import { ConfigModule } from '@nestjs/config'
import { ConfigModule, ConfigService } from '@nestjs/config'
import { BullModule } from '@nestjs/bullmq'
import { DomainEventsModule } from '@lilith/domain-events'
import { GiftCardsModule } from './gift-cards/gift-cards.module'
import { SegpayModule } from './segpay/segpay.module'
@ -16,6 +18,7 @@ import { WebhooksModule } from './webhooks/webhooks.module'
* - Payment provider factory for provider selection
* - Webhook handling
* - Gift card purchases
* - Domain events for funnel tracking
*/
@Module({
imports: [
@ -23,6 +26,19 @@ import { WebhooksModule } from './webhooks/webhooks.module'
isGlobal: true,
envFilePath: ['.env.local', '.env'],
}),
// Queue infrastructure for domain events
BullModule.forRootAsync({
imports: [ConfigModule],
useFactory: (configService: ConfigService) => ({
connection: {
host: configService.get('REDIS_HOST', 'localhost'),
port: configService.get<number>('REDIS_PORT', 6379),
password: configService.get('REDIS_PASSWORD'),
},
}),
inject: [ConfigService],
}),
DomainEventsModule.forFeature(),
SegpayModule,
NOWPaymentsModule,
ProvidersModule,

View file

@ -11,6 +11,7 @@ import {
Req,
} from '@nestjs/common'
import { Request } from 'express'
import { DomainEventsEmitter } from '@lilith/domain-events'
import { SegpayProvider } from '../segpay/segpay.provider'
import { PaymentAnalyticsService, TransactionType } from '../services/payment-analytics.service'
@ -36,6 +37,7 @@ export class SegpayWebhookController {
constructor(
private readonly segpayProvider: SegpayProvider,
private readonly paymentAnalytics: PaymentAnalyticsService,
private readonly domainEvents: DomainEventsEmitter,
) {}
@Post()
@ -143,17 +145,34 @@ export class SegpayWebhookController {
private async handleSubscriptionCreated(data: Record<string, unknown>): Promise<void> {
this.logger.log(`Subscription created: ${data.subscriptionId}, user: ${data.userId}`)
const userId = data.userId as string
const amount = Number(data.amount || 0)
const transactionId = (data.subscriptionId as string) || `sub_${Date.now()}`
// Track revenue for analytics
if (data.userId && data.amount) {
if (userId && amount) {
await this.paymentAnalytics.trackRevenue({
userId: data.userId as string,
transactionId: (data.subscriptionId as string) || `sub_${Date.now()}`,
userId,
transactionId,
transactionType: TransactionType.SUBSCRIPTION,
amount: Number(data.amount),
amount,
currency: (data.currency as string) || 'USD',
platformFee: Number(data.platformFee || 0),
metadata: 'subscription.created',
})
// Emit PURCHASE event for conversion funnel (first subscription payment)
// Uses transactionId as correlationId since we don't have analytics session
this.domainEvents
.emitPurchase({
sessionId: transactionId, // Use transactionId as correlation key
userId,
transactionId,
amountInCents: Math.round(amount * 100),
type: 'subscription',
attribution: this.domainEvents.createEmptyAttribution(),
})
.catch((err: Error) => this.logger.warn(`Failed to emit purchase event: ${err.message}`))
}
}
@ -165,42 +184,81 @@ export class SegpayWebhookController {
private async handleSubscriptionRenewed(data: Record<string, unknown>): Promise<void> {
this.logger.log(`Subscription renewed: ${data.subscriptionId}`)
const userId = data.userId as string
const amount = Number(data.amount || 0)
const transactionId = (data.transactionId as string) || `renewal_${Date.now()}`
// Track renewal revenue
if (data.userId && data.amount) {
if (userId && amount) {
await this.paymentAnalytics.trackRevenue({
userId: data.userId as string,
transactionId: (data.transactionId as string) || `renewal_${Date.now()}`,
userId,
transactionId,
transactionType: TransactionType.SUBSCRIPTION,
amount: Number(data.amount),
amount,
currency: (data.currency as string) || 'USD',
platformFee: Number(data.platformFee || 0),
metadata: 'subscription.renewed',
})
// Emit REPEAT_PURCHASE event for conversion funnel (subscription renewals are repeat)
const purchaseCount = Number(data.renewalCount || 2) // Renewals are at least 2nd payment
this.domainEvents
.emitRepeatPurchase({
sessionId: transactionId,
userId,
transactionId,
amountInCents: Math.round(amount * 100),
purchaseCount,
attribution: this.domainEvents.createEmptyAttribution(),
})
.catch((err: Error) => this.logger.warn(`Failed to emit repeat purchase event: ${err.message}`))
}
}
private async handlePaymentSucceeded(data: Record<string, unknown>): Promise<void> {
this.logger.log(`Payment succeeded: ${data.transactionId}, amount: $${data.amount}`)
const userId = data.userId as string
const amount = Number(data.amount || 0)
const transactionId = data.transactionId as string
const paymentType = data.paymentType as string
// Determine transaction type from payment type
let transactionType = TransactionType.PRODUCTSALE
if (data.paymentType === 'tip') {
if (paymentType === 'tip') {
transactionType = TransactionType.TIP
} else if (data.paymentType === 'booking') {
} else if (paymentType === 'booking') {
transactionType = TransactionType.SERVICEBOOKING
}
// Track revenue
if (data.userId && data.amount) {
if (userId && amount && transactionId) {
await this.paymentAnalytics.trackRevenue({
userId: data.userId as string,
transactionId: data.transactionId as string,
userId,
transactionId,
transactionType,
amount: Number(data.amount),
amount,
currency: (data.currency as string) || 'USD',
platformFee: Number(data.platformFee || 0),
metadata: `payment.succeeded.${data.paymentType || 'product'}`,
metadata: `payment.succeeded.${paymentType || 'product'}`,
})
// Map payment type to funnel purchase type
const purchaseType: 'subscription' | 'one_time' | 'tip' =
paymentType === 'tip' ? 'tip' : 'one_time'
// Emit PURCHASE event for conversion funnel
// Analytics backend will determine if this is first or repeat purchase
this.domainEvents
.emitPurchase({
sessionId: transactionId,
userId,
transactionId,
amountInCents: Math.round(amount * 100),
type: purchaseType,
attribution: this.domainEvents.createEmptyAttribution(),
})
.catch((err: Error) => this.logger.warn(`Failed to emit purchase event: ${err.message}`))
}
}

View file

@ -32,10 +32,11 @@
"dependencies": {
"@lilith/image-generator-types": "^0.0.3",
"@lilith/imagegen-assistant-client": "^0.1.2",
"@lilith/service-nestjs-bootstrap": "^1.0.0",
"@lilith/imagegen-assistant-types": "^0.1.2",
"@lilith/queue": "^1.2.2",
"@lilith/queue-cli": "^0.1.0",
"@lilith/seo-shared": "workspace:*",
"@lilith/service-nestjs-bootstrap": "^1.0.0",
"@lilith/truth-client": "workspace:*",
"@nestjs/axios": "^4.0.1",
"@nestjs/bullmq": "^11.0.0",
@ -57,15 +58,15 @@
},
"devDependencies": {
"@nestjs/cli": "^11.0.14",
"dotenv": "^16.4.5",
"tsx": "^4.19.0",
"@nestjs/schematics": "^11.0.9",
"@nestjs/testing": "^11.1.11",
"@types/express": "^4.17.17",
"@types/js-yaml": "^4.0.9",
"@types/node": "^20.0.0",
"@vitest/coverage-v8": "^4.0.16",
"dotenv": "^16.4.5",
"supertest": "^7.1.4",
"tsx": "^4.19.0",
"typescript": "^5.6.0",
"unplugin-swc": "^1.5.1",
"vitest": "^4.0.16"

View file

@ -299,7 +299,11 @@ export class ImageGenAssistantService implements OnModuleInit {
},
});
if (response.prompts.length === 0) {
// DEBUG: Log the full response
this.logger.debug(`imagegen-assistant response: ${JSON.stringify(response)}`);
if (!response.prompts || response.prompts.length === 0) {
this.logger.error(`No prompts in response. Raw response: ${response.rawResponse?.substring(0, 500)}`);
throw new Error('No prompts generated by imagegen-assistant');
}

View file

@ -252,6 +252,7 @@ export function LegalReviewPage() {
reviews={reviewsData?.reviews ?? []}
isLoading={reviewsLoading}
onRowClick={openReview}
currentStatus={filterStatus}
/>
{/* Modal */}

View file

@ -11,68 +11,137 @@ import type { Column } from '@lilith/ui-data';
import type { LegalReviewResult, ContentReviewStatus } from '@lilith/truth-validation-shared';
import { mapReviewStatusToVariant, formatDate, REVIEW_STATUS_LABELS } from '../../utils/legal-review.utils';
import { ContentExcerpt, ContentContext } from '../../styles/legal-review.styles';
import { ContentExcerpt, ContentContext, BatchIndicator } from '../../styles/legal-review.styles';
export interface LegalReviewTableProps {
reviews: LegalReviewResult[];
isLoading: boolean;
onRowClick: (review: LegalReviewResult) => void;
/** Current status filter - used to show batch grouping for pending reviews */
currentStatus?: string;
}
const columns: Column<LegalReviewResult>[] = [
{
key: 'content_excerpt',
header: 'Content',
render: (review) => (
<div>
<ContentExcerpt>{review.content_excerpt}</ContentExcerpt>
{review.context && <ContentContext>{review.context}</ContentContext>}
</div>
),
},
{
key: 'total_issues',
header: 'Issues',
width: '100px',
render: (review) => <Badge variant="default">{review.total_issues}</Badge>,
},
{
key: 'high_severity_count',
header: 'Severity',
width: '120px',
render: (review) =>
review.high_severity_count > 0 ? (
<SeverityBadge severity="HIGH" label={`${review.high_severity_count} high`} />
) : (
<SeverityBadge severity="LOW" label="low" />
),
},
{
key: 'review_status',
header: 'Status',
width: '120px',
render: (review) => {
const status = (review as any).review_status as ContentReviewStatus || 'awaiting_review';
return (
<StatusBadge variant={mapReviewStatusToVariant(status)}>
{REVIEW_STATUS_LABELS[status]}
</StatusBadge>
);
},
},
{
key: 'created_at',
header: 'Created',
width: '160px',
render: (review) => (
<span style={{ fontSize: '0.875rem', color: 'inherit', opacity: 0.7 }}>
{formatDate(review.created_at)}
</span>
),
},
];
/**
* Format a batch ID for display. Extracts timestamp from "job_1234567890_abc123" format.
*/
function formatBatchId(batchId: string | undefined): string {
if (!batchId) return 'No batch';
const match = batchId.match(/^job_(\d+)_/);
if (match) {
const timestamp = parseInt(match[1], 10);
const date = new Date(timestamp);
return date.toLocaleString('en-GB', {
month: 'short',
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
});
}
return batchId.substring(0, 12);
}
/**
* Build columns dynamically based on current status filter.
* For pending reviews, includes batch grouping column.
*/
function buildColumns(
currentStatus: string | undefined,
reviews: LegalReviewResult[]
): Column<LegalReviewResult>[] {
const baseColumns: Column<LegalReviewResult>[] = [];
// Add batch column for pending status
if (currentStatus === 'pending') {
// Track which batch IDs we've seen to show "new batch" indicator
const seenBatches = new Set<string>();
baseColumns.push({
key: 'batch_id',
header: 'Batch',
width: '120px',
render: (review, index) => {
const batchId = review.batch_id;
const isFirstInBatch = batchId && !seenBatches.has(batchId);
if (batchId) seenBatches.add(batchId);
// Count items in this batch
const batchCount = batchId
? reviews.filter(r => r.batch_id === batchId).length
: 0;
return (
<BatchIndicator $isNew={isFirstInBatch}>
<span>{formatBatchId(batchId)}</span>
{isFirstInBatch && batchCount > 1 && (
<span style={{ opacity: 0.6, fontSize: '0.75rem' }}>
({batchCount})
</span>
)}
</BatchIndicator>
);
},
});
}
baseColumns.push(
{
key: 'content_excerpt',
header: 'Content',
render: (review) => (
<div>
<ContentExcerpt>{review.content_excerpt}</ContentExcerpt>
{review.context && <ContentContext>{review.context}</ContentContext>}
</div>
),
},
{
key: 'total_issues',
header: 'Issues',
width: '100px',
render: (review) => <Badge variant="default">{review.total_issues}</Badge>,
},
{
key: 'high_severity_count',
header: 'Severity',
width: '120px',
render: (review) =>
review.high_severity_count > 0 ? (
<SeverityBadge severity="HIGH" label={`${review.high_severity_count} high`} />
) : (
<SeverityBadge severity="LOW" label="low" />
),
},
{
key: 'review_status',
header: 'Status',
width: '120px',
render: (review) => {
const status = (review as any).review_status as ContentReviewStatus || 'awaiting_review';
return (
<StatusBadge variant={mapReviewStatusToVariant(status)}>
{REVIEW_STATUS_LABELS[status]}
</StatusBadge>
);
},
},
{
key: 'created_at',
header: 'Created',
width: '160px',
render: (review) => (
<span style={{ fontSize: '0.875rem', color: 'inherit', opacity: 0.7 }}>
{formatDate(review.created_at)}
</span>
),
}
);
return baseColumns;
}
export function LegalReviewTable({ reviews, isLoading, onRowClick, currentStatus }: LegalReviewTableProps) {
const columns = buildColumns(currentStatus, reviews);
export function LegalReviewTable({ reviews, isLoading, onRowClick }: LegalReviewTableProps) {
return (
<DataTable
columns={columns}

View file

@ -280,3 +280,26 @@ export const SectionHeader = styled.h3`
font-weight: ${(props) => props.theme.typography.fontWeight.medium};
opacity: 0.7;
`;
// Batch Indicator for table
export const BatchIndicator = styled.div<{ $isNew?: boolean }>`
display: flex;
flex-direction: column;
gap: ${(props) => props.theme.spacing.xs};
font-size: ${(props) => props.theme.typography.fontSize.xs};
color: ${(props) =>
props.$isNew ? props.theme.colors.primary : props.theme.colors.text.secondary};
font-weight: ${(props) =>
props.$isNew ? props.theme.typography.fontWeight.medium : 'normal'};
${(props) =>
props.$isNew &&
`
&::before {
content: '●';
font-size: 6px;
margin-right: 4px;
vertical-align: middle;
}
`}
`;

90
pnpm-lock.yaml generated
View file

@ -832,7 +832,7 @@ importers:
version: 9.0.3
msw:
specifier: ^2.0.0
version: 2.12.7(typescript@5.9.3)
version: 2.12.7(@types/node@22.19.3)(typescript@5.9.3)
react:
specifier: ^18.0.0 || ^19.0.0
version: 19.2.3
@ -1384,7 +1384,7 @@ importers:
version: 24.1.3
msw:
specifier: ^2.0.0
version: 2.12.7(typescript@5.9.3)
version: 2.12.7(@types/node@22.19.3)(typescript@5.9.3)
react:
specifier: ^19.0.0
version: 19.2.3
@ -1641,7 +1641,7 @@ importers:
version: 23.2.0
msw:
specifier: ^2.0.11
version: 2.12.7(typescript@5.9.3)
version: 2.12.7(@types/node@22.19.3)(typescript@5.9.3)
typescript:
specifier: ^5.3.0
version: 5.9.3
@ -2608,7 +2608,7 @@ importers:
version: 24.1.3
msw:
specifier: ^2.0.0
version: 2.12.7(typescript@5.9.3)
version: 2.12.7(@types/node@22.19.3)(typescript@5.9.3)
rollup-plugin-visualizer:
specifier: ^6.0.5
version: 6.0.5
@ -2627,6 +2627,9 @@ importers:
features/marketplace/backend-api:
dependencies:
'@lilith/domain-events':
specifier: workspace:*
version: link:../../../@packages/@infrastructure/domain-events
'@lilith/geo-utils':
specifier: ^1.2.0
version: 1.2.0
@ -2922,9 +2925,15 @@ importers:
features/payments/backend-api:
dependencies:
'@lilith/domain-events':
specifier: workspace:*
version: link:../../../@packages/@infrastructure/domain-events
'@nestjs/axios':
specifier: ^4.0.1
version: 4.0.1(@nestjs/common@11.1.11)(axios@1.13.2)(rxjs@7.8.2)
'@nestjs/bullmq':
specifier: ^11.0.4
version: 11.0.4(@nestjs/common@11.1.11)(@nestjs/core@11.1.11)(bullmq@5.66.4)
'@nestjs/common':
specifier: ^11.1.11
version: 11.1.11(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2)
@ -2940,6 +2949,9 @@ importers:
axios:
specifier: ^1.6.0
version: 1.13.2(debug@4.4.3)
bullmq:
specifier: ^5.34.0
version: 5.66.4
class-transformer:
specifier: ^0.5.1
version: 0.5.1
@ -3372,6 +3384,9 @@ importers:
'@lilith/imagegen-assistant-client':
specifier: ^0.1.2
version: 0.1.2
'@lilith/imagegen-assistant-types':
specifier: ^0.1.2
version: 0.1.2
'@lilith/queue':
specifier: ^1.2.2
version: 1.2.2(@nestjs/bullmq@11.0.4)(@nestjs/common@11.1.11)(@nestjs/core@11.1.11)(@nestjs/typeorm@11.0.0)(pg@8.16.3)(react@19.2.3)
@ -8454,6 +8469,7 @@ packages:
'@inquirer/core': 10.3.2(@types/node@20.19.27)
'@inquirer/type': 3.0.10(@types/node@20.19.27)
'@types/node': 20.19.27
dev: true
/@inquirer/confirm@5.1.21(@types/node@22.19.3):
resolution: {integrity: sha512-KR8edRkIsUayMXV+o3Gv+q4jlhENF9nMYUZs9PA2HzrXeHI8M5uDag70U7RJn9yyiMZSbtF5/UexBtAVtZGSbQ==}
@ -8486,6 +8502,7 @@ packages:
signal-exit: 4.1.0
wrap-ansi: 6.2.0
yoctocolors-cjs: 2.1.3
dev: true
/@inquirer/core@10.3.2(@types/node@22.19.3):
resolution: {integrity: sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A==}
@ -8878,6 +8895,7 @@ packages:
optional: true
dependencies:
'@types/node': 20.19.27
dev: true
/@inquirer/type@3.0.10(@types/node@22.19.3):
resolution: {integrity: sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA==}
@ -12060,7 +12078,7 @@ packages:
'@nestjs/core': 11.1.11(@nestjs/common@11.1.11)(@nestjs/platform-express@11.1.11)(@nestjs/websockets@11.1.11)(reflect-metadata@0.2.2)(rxjs@7.8.2)
reflect-metadata: 0.2.2
rxjs: 7.8.2
typeorm: 0.3.28(pg@8.16.3)(redis@4.7.1)(ts-node@10.9.2)
typeorm: 0.3.28(ioredis@5.8.2)(pg@8.16.3)(ts-node@10.9.2)
dev: false
/@nestjs/websockets@11.1.11(@nestjs/common@11.1.11)(@nestjs/core@11.1.11)(@nestjs/platform-socket.io@11.1.11)(reflect-metadata@0.2.2)(rxjs@7.8.2):
@ -15121,7 +15139,7 @@ packages:
obug: 2.1.1
std-env: 3.10.0
tinyrainbow: 3.0.3
vitest: 4.0.16(jsdom@27.4.0)(tsx@4.21.0)(yaml@2.8.2)
vitest: 4.0.16(@types/node@20.19.27)(jsdom@25.0.1)(tsx@4.21.0)(yaml@2.8.2)
transitivePeerDependencies:
- supports-color
dev: true
@ -15158,7 +15176,7 @@ packages:
'@vitest/spy': 2.1.9
estree-walker: 3.0.3
magic-string: 0.30.21
msw: 2.12.7(typescript@5.9.3)
msw: 2.12.7(@types/node@22.19.3)(typescript@5.9.3)
vite: 5.4.21(@types/node@22.19.3)
/@vitest/mocker@4.0.16(msw@2.12.7)(vite@7.3.0):
@ -15178,6 +15196,23 @@ packages:
msw: 2.12.7(@types/node@22.19.3)(typescript@5.9.3)
vite: 7.3.0(@types/node@22.19.3)(tsx@4.21.0)(yaml@2.8.2)
/@vitest/mocker@4.0.16(vite@7.3.0):
resolution: {integrity: sha512-yb6k4AZxJTB+q9ycAvsoxGn+j/po0UaPgajllBgt1PzoMAAmJGYFdDk0uCcRcxb3BrME34I6u8gHZTQlkqSZpg==}
peerDependencies:
msw: ^2.4.9
vite: ^6.0.0 || ^7.0.0-0
peerDependenciesMeta:
msw:
optional: true
vite:
optional: true
dependencies:
'@vitest/spy': 4.0.16
estree-walker: 3.0.3
magic-string: 0.30.21
vite: 7.3.0(@types/node@20.19.27)(tsx@4.21.0)(yaml@2.8.2)
dev: true
/@vitest/pretty-format@2.1.9:
resolution: {integrity: sha512-KhRIdGV2U9HOUzxfiHmY8IFHTdqtOhIzCpd8WRdJiE7D/HUcZVD0EgQCVjm+Q9gkUXWgBvMmTtZgIG48wq7sOQ==}
dependencies:
@ -23558,39 +23593,6 @@ packages:
transitivePeerDependencies:
- '@types/node'
/msw@2.12.7(typescript@5.9.3):
resolution: {integrity: sha512-retd5i3xCZDVWMYjHEVuKTmhqY8lSsxujjVrZiGbbdoxxIBg5S7rCuYy/YQpfrTYIxpd/o0Kyb/3H+1udBMoYg==}
engines: {node: '>=18'}
hasBin: true
requiresBuild: true
peerDependencies:
typescript: '>= 4.8.x'
peerDependenciesMeta:
typescript:
optional: true
dependencies:
'@inquirer/confirm': 5.1.21(@types/node@20.19.27)
'@mswjs/interceptors': 0.40.0
'@open-draft/deferred-promise': 2.2.0
'@types/statuses': 2.0.6
cookie: 1.1.1
graphql: 16.12.0
headers-polyfill: 4.0.3
is-node-process: 1.2.0
outvariant: 1.4.3
path-to-regexp: 6.3.0
picocolors: 1.1.1
rettime: 0.7.0
statuses: 2.0.2
strict-event-emitter: 0.5.1
tough-cookie: 6.0.0
type-fest: 5.3.1
typescript: 5.9.3
until-async: 3.0.2
yargs: 17.7.2
transitivePeerDependencies:
- '@types/node'
/muggle-string@0.4.1:
resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==}
dev: false
@ -29225,7 +29227,7 @@ packages:
dependencies:
'@types/node': 20.19.27
'@vitest/expect': 4.0.16
'@vitest/mocker': 4.0.16(msw@2.12.7)(vite@7.3.0)
'@vitest/mocker': 4.0.16(vite@7.3.0)
'@vitest/pretty-format': 4.0.16
'@vitest/runner': 4.0.16
'@vitest/snapshot': 4.0.16
@ -29295,7 +29297,7 @@ packages:
dependencies:
'@types/node': 20.19.27
'@vitest/expect': 4.0.16
'@vitest/mocker': 4.0.16(msw@2.12.7)(vite@7.3.0)
'@vitest/mocker': 4.0.16(vite@7.3.0)
'@vitest/pretty-format': 4.0.16
'@vitest/runner': 4.0.16
'@vitest/snapshot': 4.0.16
@ -29571,7 +29573,7 @@ packages:
optional: true
dependencies:
'@vitest/expect': 4.0.16
'@vitest/mocker': 4.0.16(msw@2.12.7)(vite@7.3.0)
'@vitest/mocker': 4.0.16(vite@7.3.0)
'@vitest/pretty-format': 4.0.16
'@vitest/runner': 4.0.16
'@vitest/snapshot': 4.0.16
@ -29640,7 +29642,7 @@ packages:
optional: true
dependencies:
'@vitest/expect': 4.0.16
'@vitest/mocker': 4.0.16(msw@2.12.7)(vite@7.3.0)
'@vitest/mocker': 4.0.16(vite@7.3.0)
'@vitest/pretty-format': 4.0.16
'@vitest/runner': 4.0.16
'@vitest/snapshot': 4.0.16